PowerShell 'Or' Statement - powershell

I'm trying to go through the Active Directory, and grab users who meet a certain criteria. I want to get users have either Manager A or Manager B, but I'm not sure how to implement the or statement. Here's my code:
Get-ADUser -Filter * -Properties country, extensionattribute9 | if (extensionattribute9 -eq 'Smith, Joe') or (extensionattribute9 -eq 'Doe, John') {select extensionsattribute9, country}
In this code, it doesn't recognize extensionattribute9, which gives you the user's manager.
I also tried attempted using where instead of if, but to no avail.

The operator is -or, not or. See about_Logical_Operators. Also, if statements don't read from the pipeline. Either put the if statement in a ForEach-Object loop:
... | ForEach-Object {
if ($_.extensionattribute9 -eq 'Smith, Joe' -or $_.extensionattribute9 -eq 'Doe, John') {
$_ | select extensionsattribute9, country
}
}
or use a Where-Object statement instead:
... | Where-Object {
$_.extensionattribute9 -eq 'Smith, Joe' -or
$_.extensionattribute9 -eq 'Doe, John'
} | Select-Object extensionsattribute9, country
And you can't use property names by themselves. Use the current object variable ($_) to access properties of the current object.
For checking if an attribute has one of a given number of values you can also use the -contains operator instead of doing multiple comparisons:
'Smith, Joe', 'Doe, John' -contains $_.extensionattribute9

Related

Question about pipeline variables and pipeline shortcut(s)?

In the creation of a script I'm writing, I ran across the use of what appears to be a pipeline shortcut that I have never seen before.
For example:
$acl | select -expandproperty Access | ? {$_.IdentityReference -eq "AD\$Group"}
can be shortened to
$acl.Access.Where({$_.IdentityReference -eq "AD\$Group"})
What is this? $acl.Access makes sense to me as it's just a property reference but I do not see any such .Where() method being available on this object. As such it seems .Where({}) is some sort of pipeline shortcut for " | Where-Object {}". Where can I find documentation for such a thing? Do others like this exist?
Second question. I have a need to add a calculated property to my results, and within the property expression I have to perform piping as well, so where I would typically just reference $_ from the parent pipeline, this is lost within the expression statement.
I can get around this by using -PipelineVariable up within the pipeline, but it seems this only works when used with cmdlets and is not available when starting a pipeline with a variable (which I do often). Is there a way around this?
This works:
$acl = Get-Acl -Path "AD:\$dn"
$acl | select -expandproperty access -PipelineVariable AccessRight | ? {$_.IdentityReference -eq "AD\$Group"} | select *, #{N='ObjectTypeName';E={($ADGuidMap.GetEnumerator() | ? {$_.Value -eq $AccessRight.ObjectType}).Name}}
I'd really like to be able to do this with the shortcut as anywhere I can shorten my oneliner I would like to do. Unfortunately the following will not work as I cannot use pipelinevariable at this location.
$acl.Access.Where({$_.IdentityReference -eq "AD\$Group"}) -PipeLineVariable AccessRight | select *, #{N='ObjectTypeName';E={($ADGuidMap.GetEnumerator() | ? {$_.Value -eq $AccessRight.ObjectType}).Name}}
It's always bugged me about not being able to use Pipeline variables outside of cmdlets so I finally figured I'd ask if there was some sort of way around that.
As for the first question:
The .Where() and .ForEach() methods are intrinsic members, i.e. members that PowerShell implicitly makes available on objects of any type.
While they function similarly to their cmdlet counterparts, Where-Object and ForEach-Object, there are important differences.
See this answer for more information.
As an aside: You could have simplified your command even when using the Where-Object cmdlet, namely with simplified syntax:
$acl.Access | Where-Object IdentityReference -eq "AD\$Group"
As for the second question:
Because the .Where() method isn't a cmdlet, you cannot use the common -PipelineVariable parameter with it.
Given that .Access typically returns multiple objects, using the pipeline with -PipelineVariable enables an elegant solution.
If you do want to avoid the pipeline (operator), you can combine the .Where() and .ForEach() methods as follows, with the help of an intermediate (regular) variable:
$acl.Access.
Where({ $_.IdentityReference -eq "AD\$Group" }).
ForEach({
$aclEntry = $_
Select-Object -InputObject $aclEntry -Property *,
#{
N = 'ObjectTypeName'
E = { ($ADGuidMap.GetEnumerator().Where({ $_.Value -eq $aclEntry.ObjectType })).Name }
}
})
Update:
As you've discovered yourself, if you stick with a pipeline, you can combine -PipeLineVariable with Write-Output in order to capture each element of an array / collection in a pipeline variable as it flows through the pipeline, for use a later script block, as the following (contrived) example shows:
$array = 0..3
Write-Output $array -Pipeline arrayElement |
Where-Object { 0 -eq $_ % 2 } | # filter out odd numbers
ForEach-Object { $_ + 1 } | # add 1
ForEach-Object { "pipeline variable / `$_: $arrayElement / $_" }
Output, which shows that each element of input array $array was individually captured in pipeline variable $arrayElement:
pipeline variable / $_: 0 / 1
pipeline variable / $_: 2 / 3

I am trying to add members to a AD group based on a value in the "title" attribute [duplicate]

This question already has answers here:
I am trying to add members to a AD group based on a value in the "title" attribute
(3 answers)
Closed 2 months ago.
I am trying to add members to a AD group based on a value in the "title" attribute. I have about 30 different titles I want to use. Is there a way to write the command without 30 "OR" statements?
Thanks
Get-ADuser -filter {(title -eq "SECSCH") -or (title -eq "SEC12") -or (title -eq
"LTOSEC") -or (title -eq "LTO12")} | %{Add-ADGroupMember "SDK test print color"
$_.SamAccountName}
Also, for another group I would like All "custod" in title except if their "location" attribute is "85c" or location "42c" Below is where i am at.
Get-ADuser -filter {(title -eq "custod") -and (locationNumber -ne "85c") -or (title -eq
"custod") -and (locationNumber -ne "42c")} | %{Add-ADGroupMember "SDK test print
convert" $_.SamAccountName}
You can use the -in to specify list of values to compare with the title attribute
Here's an example :
$titles = "SECSCH", "SEC12", "LTOSEC", "LTO12"
Get-ADUser -Filter {title -in $titles} | ForEach-Object {
Add-ADGroupMember "SDK test print color" $_.SamAccountName
}
on the same strategy, you can exclude locations with operator -notin
$excludedLocations = "85c", "42c"
Get-ADUser -Filter {title -eq "custod" -and locationNumber -notin $excludedLocations} | ForEach-Object {
Add-ADGroupMember "SDK test print convert" $_.SamAccountName
}
I use a procedural method to build this kind of thing into an LDAPfilter, which is easier to construct than a load of -or statements, I feel.
$titles = #'
SECSCH
LTOSEC
???12
'# -split '\r?\n'
# begin filter
## (&(samAccountType=805306368) is not really needed for Get-Aduser
$filter = "(&(samAccountType=805306368)(|"
# append each title
$titles | Foreach { $filter += "(title=$_)"}
# end filter
$filter += "))"
# filter = (&(samAccountType=805306368)(|(title=SECSCH)(title=SEC12)(title=LTOSEC)(title=???12)))
Get-Aduser -ldapfilter $filter
Notice I slipped a wildcard in the list. For example, that one will catch all titles exactly 5 chars long ending with "12". There may be shortcuts like that you can use in your environment.
If you use a wildcard query, but want to exclude some possible results, you can always add NOT clauses (make sure they're inside an AND clause, not inside the OR clause!):
(&(samAccountType=805306368)(!title=SOP12)(|...
Also remember -searchbase to start from specific OUs if that might help you use wildcards or just better target sets of users with less noise/filtering required.

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.

Troubles with substring, trim, trimend

I'm trying to take an array of email addresses (in the form of username#company.com) which is generated from:
$users = get-MSOLUser -All | where {$_.isLicensed -eq "TRUE" -and $_.Licenses.AccountSKUID -eq "my_license"} | select userprincipalname
And get just the username from each. I start with username#company.com and want to end up with username. I have tried various ways using substring, Trim, TrimEnd, etc and can't get any of them working.
$username = $users | %{$_.substring(0,$users.length - 12)}
$users | %{$_.trimend("#company.com")}
$users | %{$_.trimend(12)}
All of the above give errors including the two below.
Method invocation failed because [Selected.Microsoft.Online.Administration.User] does not
contain a method named substring.
Method invocation failed because [Selected.Microsoft.Online.Administration.User] does not
contain a method named trimend.
What am I doing wrong with the syntax, or is there something else, like a module I haven't imported, or how my syntax is trying to work with an array?
This will return you a list of all usernames (without domain) that fulfills your conditions:
$users = Get-MSOLUser -All |
Where-Object {$_.isLicensed -eq "TRUE" -and $_.Licenses.AccountSKUID -eq "my_license"} |
ForEach-Object { $_.userprincipalname -replace '#.*' }
Well, you need to work with the property, not with the object, so you would probably want to do something like:
select -expandproperty userprincipalname
but that would create an array of userprincipalnames, so no other attributes.
When you run get-MSOLUser you get back an object, with a bunch of properties. When you do select -expandproperty you are getting back only certain property, but not an object itself. You are getting back a system.string object. And that object has all those methods you are trying to invoke.

Powershell using where-object

I am using Powershell and am having trouble with the Where-Object cmdlet. I currently select * and then want to only output when a field is equal to Alabama. This field could be under any column, not just one.
This is what I have:
select * | where {$_.state_name -eq 'Alabama'} .
This works for state_name, but i cant get all columns without doing them individually. I've tried where{$_ -eq....} but that doesn't work.
Kind of a hack, but:
select * | where {($_ | ConvertTo-Csv -NoTypeInformation)[1] -like '*"Alabama"*'}
You have to iterate over all object's properties and check if it contains word 'Alabama'.
Example:
# Import CSV file and feed it to the pipeline
Import-Csv -Path .\My.csv |
# For each object
ForEach-Object {
# Psobject.Properties returns all object properties
# (Psobject.Properties).Value returns only properties' values
# -contains operator checks if array of values contains exact string 'Alabama'
# You can also use -like operator with wildcards, i.e. -like '*labama'
if(($_.PSObject.Properties).Value -contains 'Alabama')
{
# If any of the object properties contain word 'Alabama',
# write it to the pipeline, else do nothing.
$_
}
}