Powershell get only properties matching string pattern from Get-ADUser - powershell

I was trying to get all properties containing the string "home" from an AD User (HomeDirectory, HomeDrive etc.). I can make that work by doing the following based off of this post:
Get-ADUser -Identity MyUser -Properties * | Select-Object -Property "*home*"
However, this will bog down the system if I'm doing it in a for-loop since it will fetch all properties first, and then after that filter out the ones that match the string "home" anywhere in the property name.
Is there a way to do this filtering already in the Get-ADUser call to reduce the amount of information being sent? I guess the more generic question would be: is there a way in Powershell to fetch only properties of an object that matches a specific string pattern?

You can create this pattern yourself by calling get-aduser -id myuser -properties * | % { $_.propertynames -match "home" } - doing this ONCE you can store the outcome into an array then supply this to further get-aduser calls.
$proplist=get-aduser -id myuser -properties * | % { $_.propertynames -match "home" }
get-aduser -properties $proplist

Another approach could be to first get an array of all LDAP attribute names from the AD Schema:
function Get-ADUserAttributeNames {
# First, get all AD user attributes defined in the Active Directory schema
$searchBase = (Get-ADRootDSE).SchemaNamingContext
$schemaAttribs = (Get-ADObject -SearchBase $searchBase -Filter "name -like 'user'" -Properties MayContain,SystemMayContain |
Select-Object #{Name = 'Attribs'; Expression = {$_.maycontain + $_.systemmaycontain}}).Attribs
# Next, get all created user attributes. These are not defined in the schema, but calculated when asked for
$flagsAttribs = (Get-ADObject -SearchBase $searchBase -LDAPFilter '(systemFlags:1.2.840.113556.1.4.803:=4)' -Properties systemFlags).Name
return ($schemaAttribs + $flagsAttribs) | Sort-Object
}
$userAttribs = Get-ADUserAttributeNames
In subsequent calls, use the returned $userAttribs array like this:
$homeAttribs = $userAttribs | Where-Object { $_ -like '*home*' }
Get-ADUser -Filter * -Properties $homeAttribs
Some explanation
This approach retrieves the list of LDAP user attribute names from the AD Schema itself, so there is no need to probe a known user. The returned attribs apply to any user object in your AD environment.
With Vesper's good answer you do need a user that you know exists, but that's no problem of course since you can simply use your own SamAccountName.
The reason I've emphasized LDAP is that Ldap attribute names are not always as self-descriptive as you would like, sometimes just one character and on other occasions ridiculously long..
That is why PowerShell maps most common atribute names to more friendly (and also case-insensitive) names.
Some examples:
LDAP PowerShell
---- ----------
l City
o Organization
cn Name
physicalDeliveryOfficeName Office
facsimileTelephoneNumber Fax
wWWHomePage HomePage
nTSecurityDescriptor CannotChangePassword
PowerShell in some cases also changes the format of an attribute to an easier to use format like with Enabled which returns a Boolean value from computing the LDAP userAccountControl (bit mask not 2) or PasswordLastSet which returns a DateTime object from ldap's pwdLastSet attribute.
The AD Schema can be extended with more attributes. Sometimes software does that (like Exchange that extends the schema with lots of msExch* atributes) but you (as administrator) can add new properties too.
The list you get with above function is therefore quite, but not completely static and can change over time.

Related

Check to See if User is part of any of certain AD groups

I'm fairly new to powershell. I'm hoping to get direction on how I can task to work.
So part of my new hire script is to ensure a user account that gets created is surely added to Office ALL group. If it doesn't get added for some reason, I need a way to identity which account didn't get added.
We have office group office name that starts with Province/state Like ON,BC,AB and end with -ALL#jbalatest.com
$ADgroupofAlloffices= $provincearray | ForEach-Object -process {Get-ADGroup -Filter "mail -like '$_-*'" -Properties Mail |
Where-Object {$_.Mail -LIKE "*-ALL#jbalatest.com"} |
Select-Object DistinguishedName | Sort-Object -Property Samaccountname}
This is my query to get all the groups by DistinguishedName. I have than more 30 groups.
Now I need away to check to see if the new hire account is part of this group as part of the final portion of over all script.
$user_groups = (Get-ADUser -Identity $ADUseraccount -Properties memberof | Select-Object memberof).memberof
I know with this I can get all the group of users.
How I check if AD user is part any of these office groups and let me know if user is not part any of these groups.
Let me know where need to do more search on finding solution for this. Scratching my head here.
Several ways to do this, here is one, first the way you're getting the Office Groups can be optimized using AD Filtering only, with some string manipulation. You can iterate over the $provArray to generate an array of strings which then gets joined and combined into a single LDAP Filter, in example with the Provinces / States provided in your question the filter would be something like this:
(|(mail=ON*-ALL#jbalatest.com)(mail=BC*-ALL#jbalatest.com)(mail=AB*-ALL#jbalatest.com))
Then after you query for all the groups, you can get an array with the DistinguishedName of all these groups ($officeGroups) which we can use to compare with the memberOf attribute of the user:
$provArray = 'ON', 'BC', 'AB' # and more here
$filter = foreach($i in $provArray) {
"(mail=$i*-ALL#jbalatest.com)"
}
$filter = "(|$(-join $filter))"
$officeGroups = (Get-ADGroup -LDAPFilter $filter).DistinguishedName
$user = (Get-ADUser 'someuser' -Properties memberof).memberof
if($user.where{ $officeGroups -contains $_ }) {
'user is a member of at least one office group'
# do something here
}
else {
'user is not a member of any office groups'
# do something here
}
Since you already have the distinguished name of all of the groups you're interested, you can just test if any of the groups that the user is a member of is in that list and go from there.
$OfficeGroup = $ADgroupofAlloffices | Where{$_.DistinguishedName -in $user_groups}
If(!$OfficeGroup){Write-Warning "User is not in an office group"}

Foreach in foreach (nested)

I'm trying to loop all disabled users through an array of groups to check if the users have membership in any of the listed groups. My thought is that for every user in the list loop them through and check if they are present in one of the listed groups. That would require nesting foreach loops, right? The output I get is like this:
...
user1
user2
user3
is not a member of group1
Here is the source code:
$dUsers = Get-ADUser -Filter {enabled -eq $false} |
FT samAccountName |
Out-String
$groups = 'Group1', 'Group2'
foreach ($dUser in $dUsers) {
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SamAccountName
if ($members -contains $dUsers) {
Write-Host "[+] $dUser is a member of $group"
} else {
Write-Host "[-] $dUser is not a member of $group"
}
}
}
I'm pulling my hair because I feel like there is a simple solution, but I'm lost.
Update:
I wanted to put all disabled users in variable $dUsers.
It actually works if I manually put users in the variable like this:
$dUsers = 'user1','user2','user3'
Which gives me the following output:
user1 is not a member of group1
user1 is not a member of group2
user2 is not a member of group1
user2 is not a member of group2
...
This makes me question how it gets "foreached" when the variable is:
$dUsers = Get-ADUser -Filter {enabled -eq $false} |
FT samAccountName |
Out-String
Anyone got a clarification on that?
Update:
This is the final code. It takes a long time to run, even with only two groups.
$dUsers = Get-ADUser -Filter {enabled -eq $false} | Select-Object -Expand SamAccountName
$groups = 'Group1', 'Group2'
Write-host '[+] Checking if any disabled user is member of any SSL groups'
Write-host '[+] This might take a while. Get a coffee!'
write-host '[+] Running...'`n
foreach ($dUser in $dUsers) {
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName
if($members -contains $dUser) {
Write-Host "$dUser is a member of $group"
} Else {
# Remove or comment out the line below to get a clutterfree list.
# Write-Host "$dUser is not a member of $group"
}
}
}
You have two issues in your code:
You're creating a single string from the Get-ADUser output. Piping the output of that cmdlet through Format-Table (alias ft) and then Out-String creates one string with a tabular display of all matching account names including the table header.
If you output $dUsers in a way that makes beginning and end of a string visible you'd see something like this (the leading and trailing == marking the beginning and end):
PS> $dUsers | ForEach-Object { "==$_==" }
==samAccountName
--------------
user1
user2
user3==
Since there is no account with a username matching this string no match can be found in any group and you're getting the output you observed.
This misuse of Format-* cmdlets is a common beginner's mistake. People get a nicely formatted string output and then try to work with that. ONLY use Format-* cmdlets when you're presenting data directly to a user, NEVER when further processing of the data is required or intended.
What you actually want is not a string with a tabular display of usernames, but an array of username strings. You get that by expanding the SamAccountName property of the user objects you get from Get-ADUser.
$dUsers = Get-ADUser ... | Select-Object -Expand SamAccountName
The second issue is probably just a typo. Your condition $members -contains $dUsers won't work, since both $members and $dUsers are arrays (after fixing the first issue, that is). The -contains operator expects an array as the first operand and a single value as the second operand.
Change
$members -contains $dUsers
to
$members -contains $dUser
Depending on what PowerShell version you are on, there is a cmdlet for this use case and others.
As for
I'm Trying to loop all disabled users
Just do...
Search-ADAccount -AccountDisabled |
Select-Object -Property Name, Enabled,
#{Name = 'GroupName';Expression = {$_.DistinguishedName.Split(',')[1] -replace 'CN='}}
# Results
Name Enabled GroupName
---- ------- ---------
...
testuser2 NewTest False Users
Guest False Users
Or different cmdlet…
# Get disabled users and their group membership, display user and group name
ForEach ($TargetUser in (Get-ADUser -Filter {Enabled -eq $false}))
{
"`n" + "-"*12 + " Showing group membership for " + $TargetUser.SamAccountName
Get-ADPrincipalGroupMembership -Identity $TargetUser.SamAccountName | Select Name
}
# Results
...
------------ Showing group membership for testuser1
Domain Users
Users
------------ Showing group membership for testuser2
Domain Users
As for ...
an array of Groups
Just select or filter the DN for the group name you want using the normal comparison operators.
As for...
Unfortunately I'm not well versed in powershell.
… be sure to spend the necessary time to get ramped up on it, to limit the amount of misconceptions, confusions, errors, etc. that you are going to encounter. There are plenty of no cost / free video and text-based training / presentations all over the web.
Example:
Videos
Use tools that will write the code for you that you can later tweak as needed.
Step-By-Step: Utilizing PowerShell History Viewer in Windows Server 2012 R2
Learning PowerShell with Active Directory Administrative Center (PowerShell History Viewer)
As well as plenty of sample scripts and modules via the MS PowerShell Script / Module Gallery.
There are two commands for the AD Groups.
First I see that you want the membership of the disabled users that is easy.
#Get the dissabled users from your AD with all their attributes (properties and select)
$dUsers = Get-ADUser -Filter {Enabled -eq $false} -Properties * | Select *
#Run a loop for each user to get the group membership
Foreach ($User in $dUsers) {
$User = $User.SamAccountName
Get-ADUser $User -Properties * | Select Name, SamAccountName, MemberOf | Format-Table -Wrap # > "D:\test\$user.txt" -HideTableHeaders
}
This one can work but I don't like the output that we get.
I prefer to run the groupmembership command and check the users.
$GroupMembers = Get-ADGroupMember "groupname"| Select Name, SamAccountName
ForEach ($User in $GroupMembers)
{
$UserProperties = Get-ADUser $User.SamAccountName -Properties * | select *
If ($UserProperties.Enabled -eq $False) {
Write-Host $UserProperties.SamAccountName
}
}
Edit:
Let me know if those fits you.
Kind regards.
The first thing you should try to check is whenever you are only interested in direct memberships or indirect ones as well. Depending on the answer the options you got availabel change a bit. You probably will encounter Distinguished Names while working on this so check out what they are if you don't know (mostly a path for an object).
If it's only direct memberships using memberOf with Get-ADUser should be sufficient. The memberOf attribute contains every direct group membership of the user with the full Distinguished Name of the group.
Get-ADUser test -Properties MemberOf | Select-Object -ExpandProperty memberOf
You can match the groups you're looking for in various ways. You could get the whole Distinguished Name of those groups or you could do a partial match. It's up to you to decide how to proceed.
If you need the indirect memberships as well you might want to split up your code to make it easier for yourself. For instance you could first find the users and save them. Afterwards find all group members of those groups (You already got that with Get-ADGroupMember) and finally compare the two.
Currently for every user you build the whole list of group members again. This approach would save a few resources as you wouldn't be doing the same queries over and over again.
Finally you could also use the MemberOf approach but get the list of every direct and indirect membership of a user using an LDAP query.
$dn = (Get-ADUser example).DistinguishedName
$userGroups = Get-ADGroup -LDAPFilter ("(member:1.2.840.113556.1.4.1941:={0})" -f $dn)
This approach uses a LDAP search query. It can be quite complex, you could also only check for one one of the groups by modifying it a bit.
In the end even your current approach should work. The problem is that you're comparing the AD object against the list of SAM Accountnames. You would need to check for the SAM Accountnames as well.
if($members -contains $dUsers.SamAccountName)
if($members -contains $dUsers | Select-Object -ExpandProperty SamAccountName)
One of these should work if you change your $dUsers as well. As it currently is you end up with a giant string. You probably can check that by checking $dUsers.length. Just drop the Format-Table and Out-String.

Multiple rows in a grid [duplicate]

This question already has answers here:
Export hashtable to CSV with the key as the column heading
(2 answers)
Closed 4 years ago.
I'm trying to list all ad group memberships of specific users. The input would be a string of logins split with a comma 'login1,login2'.
So I go over each user and list their memberships with the username as title. Somehow it only shows the first entry. Also it shows the user groups in one row and I don't know how to change that.
Code below:
$users = $logon -split ','
$q = #()
foreach ($user in $users) {
$usernm = Get-ADUser -Filter 'samAccountName -like $user' | select Name
$useraccess = Get-ADPrincipalGroupMembership $user | Select-Object Name
$userobj = New-Object PSObject
$userobj | Add-Member Noteproperty $usernm.Name $useraccess.Name
$q += $userobj
}
Expected output would be something like:
fullnameuser1 fullnameuser2 list of users goes on...
------------- ------------- ------------------------
adgroup1 adgroup3 ...
adgroup2 adgroup4
... ...
In principle this would also mean that if i typed $q.'fullnameuser1' output would be:
fullnameuser1
-------------
adgroup1
adgroup2
...
Whenever the code is ran, it will only ever add the first user's access, also returning all groups on one row. So somehow I need to go over all the group memberships and add a row for each one.
First and foremost, PowerShell does not expand variables in single-quoted strings. Because of that Get-ADUser will never find a match unless you have a user with the literal account name $user. Also, using the -like operator without wildcards produces the same results as the -eq operator. If you're looking for an exact match use the latter. You probably also need to add nested quotes.
Get-ADUser -Filter "samAccountName -eq '${user}'"
Correction: Get-ADUser seems to resolve variables in filter strings by itself. I verified and the statement
Get-ADUser -Filter 'samAccountName -eq $user'
does indeed return the user object for $user despite the string being in single quotes.
If you want a fuzzy match it's better to use ambiguous name resolution.
Get-ADUser -LDAPFilter "(anr=${user})"
You may also want to avoid appending to an array in a loop, and adding members to custom objects after creation. Both are slow operations. Collect the loop output in a variable, and specify the object properties directly upon object creation.
$q = foreach ($user in $users) {
...
New-Object -Type PSObject -Property {
$usernm.Name = $useraccess.Name
}
}
Lastly, I'd consider using the user's name as the property name bad design. That would be okay if you were building a hashtable (which is mapping unique keys to values), but for custom objects the property names should be identical for all objects of the same variety.
New-Object -Type PSObject -Property {
Name = $usernm.Name
Group = $useraccess.Name
}
Basily query all the users and store it in $users, example:
Get-ADUser -Filter * -SearchBase "dc=domain,dc=local"
And then you can export the results as csv or a table.
To Export as CSV :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | export-CSV C:\data\ADUserGroups.csv`
To Format the result as Table in the console itslef :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | Format-Table

Powershell comparison with attributes

I'm beginner in powershell and I need your help.
I need to compare the department attribute from the AD containing some text amd replacing by another value.
But it doesn't work. Do I made a mistake below? Cheers
//Find the user and save the user in the variable
$member = get-Aduser -f {GivenName -eq 'Jack'}
//check if the Departement field match with "Dep20 "
if($member.department -eq "Dep20")
{
//Set "Dep21" in department field
$member.Department = 'Dep21';
set-AdUser -f {GivenName -eq $member.givenName} -departement $member.Department;
}
Some issues with your initial script
First
Get-AdUser won't give you the property Department by default.
You could have confirmed this by actually looking at the output of your Get-AdUser statement. You do need to add it to the list of properties explicitely.
get-Aduser -f {GivenName -eq 'Jack'} -Properties Department
Also, you did make a mistake in the Set-AdUser cmdlet. The parameter name you have written, at the time of my answer, is -departement. Instead, you need to set -department.
Finally, Get-AdUser could return multiple users (or none).
Therefore, you need to account for that by checking how many $member were returned or to do a foreach to process none (if 0) or all of them the same.
At least, that part is subjective to what you need but here would be my approach.
$member = get-Aduser -Filter 'GivenName -like "Jack*"' -Properties Department
$member | foreach {
if ($member.Department -eq 'Dep20')
{
$_.Department = 'Dep21'
set-AdUser $_ -Department $_.Department;
}
}
Edit:
I modified my answer to switch the Filter parameter from a scriptblock (as your question) for a string filter as per mklement0 comment.
Because the Filter parameter is actually a string, giving it a script block will create problems on multiple occasions and you are better restrict yourself to the string type for this parameter.
See this for a more detailed explanation on the matter.

How to find *where* in ActiveDirectory an attribute is stored?

I want to use Power Query to extract a list of employee names including attributes such as username, telephone number, office number, etc.
I found an article that shows how to do just that:
http://datapigtechnologies.com/blog/index.php/pull-your-global-address-book-into-excel/
However, my question is: if I wanted to return an attribute that I know that exists, but I haven't a clue where in the Active Directory object/table model it resides, how might one perform a search to find the specific table & attribute that must be queried?
EDIT - Additional Information
"Is this more of a PowerQuery question that a PowerShell question" - I think the answer is both yes and no.
This command:
Get-ADUser <someValidUserName> - Property *
...does indeed output all the attributes for the specified user. However, as far as I can tell there is no indication in the output as to where in the AD object hierarchy each attribute resides.
From the Power Query article linked above, we see several "tables" noted in the Power Query interface to Active Directory. One such table is organizationalPerson which contains an attribute named physicalDeliveryOfficeName. It would seem that the notion of an organizationalPerson "object" isn't exclusive to Power Query, as it seems to correspond to the Ldap-Display-Name as documented here:
https://msdn.microsoft.com/en-us/library/ms683883(v=vs.85).aspx
So what I was hoping for was a means to wildcard search the AD attribute names themselves for the existence of the word "office" anywhere within any attribute name in the AD user hierarchy, and for the search results to return physicalDeliveryOfficeName as a result, including the fact that it resides within organizationalPerson
(Hopefully that makes the question a bit more clear?)
For posterity: Here is a PowerShell script (See Get Class Attributes) that will list all Active Directory classes + class attributes for a specified SamAccountName. From the list of attributes, you can then run something like the following to get the attribute values
#make a list of desired class attributes
$Properties = #('DisplayName', 'SamAccountName', 'mail', 'otherMailbox')
Get-ADUser -Filter * -SearchBase "dc=myDomain,dc=gov" -Properties $Properties | select $Properties
Credit to: http://virot.eu/getting-all-possible-classes-attributes-for-a-ad-object/
Get Class Attributes
#Run on Win 7 machine with PS 4.0 against A.D. running on a Win 2008 R2 domain controller
cls
$attributeList = New-Object System.Collections.ArrayList
$attributeListItem = [ordered]#{}
Import-Module ActiveDirectory
#Get an AD User and request objectClass
$aDObj = Get-ADUser "MyAccountName" -Properties objectClass
#get all class names
$nextClass = $aDObj.ObjectClass
$allClasses = Do
{
$currentClass = $nextClass
$nextClass = Get-ADObject -SearchBase "$((Get-ADRootDSE).SchemaNamingContext)" -Filter {lDAPDisplayName -eq $nextClass} -properties subClassOf | Select-Object -ExpandProperty subClassOf
$currentClass
}
While($currentClass -ne $nextClass)
#Get all attributes
$mandatoryAndOptionalAttributes = 'MayContain','MustContain','systemMayContain','systemMustContain'
ForEach ($class in $allClasses)
{
$classInfo = Get-ADObject -SearchBase "$((Get-ADRootDSE).SchemaNamingContext)" -Filter {lDAPDisplayName -eq $class} -properties $mandatoryAndOptionalAttributes
ForEach ($mandatoryAndOptionalAttribute in $mandatoryAndOptionalAttributes)
{
foreach ($classAttribute in $classInfo.$mandatoryAndOptionalAttribute)
{
$attributeListItem."Mandatory/Optional Attribute" = $mandatoryAndOptionalAttribute
$attributeListItem."Class" = $classInfo.Name
$attributeListItem."Class Attribute" = $classAttribute
$attributeList.add((New-Object PSObject -Property $attributeListItem)) | out-null
}
}
}
$attributeList | out-gridview -title ("All Class Attributes: " + $attributeList.count)