Get-ADUser across domains - Help needed - powershell

I have the following code:
$FilePath_Prefix = "C:\temp\UserLastLogon-"
function Msg ($Txt="") {
Write-Host "$([DateTime]::Now) $Txt"
}
#Cycle each DC and gather user account lastlogon attributes
$List = #() #Define Array
(Get-ADDomain).ReplicaDirectoryServers | Sort | % {
$DC = $_
Msg "Reading $DC"
$List += Get-ADUser -Server $_ -Filter "samaccountname -like '*'" -Properties LastLogon |
Select samaccountname, lastlogon, #{n='DC';e={$DC}}
}
Msg "Sorting for most recent lastlogon"
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$LatestLogOn += ($_.Group | Sort -prop lastlogon -Descending)[0]
}
$List.Clear()
$FileName = "$FilePath_Prefix$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).csv"
try {
$LatestLogOn |
Select samaccountname, lastlogon,
#{n='lastlogondatetime';e={[datetime]::FromFileTime($_.lastlogon)}}, DC |
Export-CSV -Path $FileName -NoTypeInformation -Force
Msg "Exported results. $FileName"
} catch {
Msg "Export Failed. $FileName"
}
I use it to interrogate AD Users for most up-to-date lastLogon information across all my domains. It works, and it works really fast.
Now, I need to get more details into my output, such as givenName and Surname lets say.
What would be the best approach to achieve this, because I don't want to interrogate redundantly all my DC's for those kind of attributes.
My idea here, is to create another array with Get-ADuser -Filter * -Properties givenName, surname, etc..etc and then bind together the two arrays. And I don't seem to get it right. Could someone help, or point me in the right direction to achieve this task.

I would fetch all the last logon information and save it into a hash table, which is designed for fast lookups. I'd try something like this:
$FileName = 'C:\temp\UserLastLogon-{0:yyyyMMdd-HHmmss}.csv' -f [DateTime]::Now
$DCs = (Get-ADDomain).ReplicaDirectoryServers | Sort-Object
# This *may* be more semantically accurate; I don't remember how it works with multiple domains
# $DC = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName | Sort-Object
# Create the hash table
$UserLogonInfo = #{}
foreach ($DC in $DCs) {
Write-Host "Reading logon data from $DC..."
# Fetch all users that have a LastLogon value from the current DC
# We specify LastLogon>=1 because some users that never log on have LastLogon of
# 0 or null, both of which would show up as Jan 1, 1601.
Get-ADUser -Server $DC -LDAPFilter '(LastLogon>=1)' -Properties LastLogon | ForEach-Object {
if (!$UserLogonInfo.ContainsKey($_.DistinguishedName)) {
# If the accountname doesn't exist, add it
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
elseif (($UserLogonInfo[$_.DistinguishedName].LastLogon -lt $_.LastLogon)) {
# If the account name exists, update it if it's more recent
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
}
}
Write-Host "Fetching user data..."
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n='LastLogonDateTime';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDateTime}},
#{n='LastLogonDC';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDC}} |
Export-CSV -Path $FileName -NoTypeInformation -Force
If an account has a blank LastLogonDateTime and blank LastLogonDC, then that account has never logged on.
It's more correct to use DistinguishedName instead of SamAccountName as the key for the $UserLogonInfo hash table, and that is essentially a required change if you are querying multiple domains at once. Note that I do mean multiple domains and not merely multiple domain controllers in the same domain (which is what I believe you're actually doing in spite of the question title).
This whole process on my domain with 3 DCs and ~10,000 users takes about 15 seconds.
Note that there are a ton of ways that LastLogon can be inaccurate. It can be updated without a full logon or without an interactive logon and some logons won't force an update of the field. If you really want to track logons, you should use security auditing for logon events.
Edit:
When we populate $UserLogonInfo we're fetching all accounts except for accounts that either don't have a LogonDate attribute at all or when that attribute is 0 or null. Each of those indicate that there has been no login. So, we know that any user that isn't in the $UserLogonInfo hash table has never logged in.
If you want to use some special value for when a user account has never logged on, you should just use an if statement and check to see if the user is in the $UserLogonInfo hash table:
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n = 'LastLogonDateTime'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDateTime } else { 'Never' }}},
#{n = 'LastLogonDC'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDC } else { 'N/A' }}} |
Export-CSV -Path $FileName -NoTypeInformation -Force

The thing is that you're actually asking your DCs for that data as it's returned by default. So the only thing to add is
, givenName, surname
to your Select-Object. Check this by running
Get-AdUser yourLogin -Properties LastLogon
You'll receive the following properties in your output: DistinguishedName Enabled GivenName LastLogon
Name
ObjectClass
ObjectGUID
SamAccountName
SID
Surname
UserPrincipalName
So the only thing you lose will be some memory. The alternative approach would be to create another array using
$names = Get-ADuser -Filter * | Select-Object SamAccountName, givenName, surname
and then match the date based on SamAccountName like this:
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$t = ($_.Group | Sort -prop lastlogon -Descending)[0]
$n = $names | Where-Object samaccountname -eq $t.samaccountname
$LatestLogOn += $t | select *, #{n="givenName";e={$n.givenName}}, #{n="surname";e={$n.surname}}
}

Related

Filtering multiple null values from Get-ADUser

I'm still working on learning powershell. I need to pull email and manager email from AD accounts in a group but exclude any records that have manager blank or email blank. I don't think I can 'where' for two where conditions, so I can get one or the other.
Get-ADGroupMember -Identity "groupname" -Recursive | Get-ADUser -properties * | where manager -NE $null | select displayname, EmailAddress, #{Name="ManagerEmail";Expression={(Get-ADUser -property Emailaddress $_.manager).emailaddress}} | export-csv -path c:\data.csv -NoTypeInformation
How would I go about getting both filtered out?
You can use and should use the Active Directory Filter or LDAP Filter for this instead of doing the filtering with powershell:
$group = (Get-ADGroup groupname).DistinguishedName
$managerMap = #{}
$params = #{
LDAPFilter = "(&(memberof:1.2.840.113556.1.4.1941:=$group)(mail=*)(manager=*))"
Properties = 'mail', 'manager'
}
Get-ADUser #params | ForEach-Object {
if(-not $managerMap.ContainsKey($_.manager)) {
$managerMap[$_.manager] = (Get-ADUser $_.manager -Properties mail).mail
}
[pscustomobject]#{
DisplayName = $_.DisplayName
EmailAddress = $_.mail
ManagerEmail = $managerMap[$_.manager]
}
} | Export-Csv 'c:\data.csv' -NoTypeInformation
Details of the LDAP Filter:
(& # AND, all conditions must be met
(memberof:1.2.840.113556.1.4.1941:=$group) # user is a member of `$group` (recursively)
(mail=*) # mail attribute is not null
(manager=*) # manager attribute is not null
) # closing AND

List properties of users using foreach comment

My goal is to list extended properties of a list of users by Display Name or SamAccountName pulling those names from a Csv. I am using the following script and it works but it either skips names in the Csv or repeats them. If I do one at a time it returns what I need but from the Csv it doesn’t. Csv has one column named Name.
Import-Csv C:\Users\Administrator\Documents\test.txt | Foreach {
Get-ADUser -Filter "DisplayName -eq '$($_.Name)'" -Properties *
} | Select-Object DisplayName, SamAccountName, Title, Department, EmailAddress, ObjectGUID | Sort-Object Displayname | FT
There is nothing wrong with your current code, except for using Import-Csv on a .txt file (test.txt), I would assume this was a typo. I've added an if condition to help you troubleshoot so at least you would know which users where not found.
You should also avoid the use of -Properties *, querying all properties for the users is inefficient and slow.
$properties = #(
'DisplayName'
'SamAccountName'
'Title'
'Department'
'EmailAddress'
'ObjectGUID'
)
Import-Csv C:\Users\Administrator\Documents\test.csv | ForEach-Object {
$adUser = Get-ADUser -Filter "DisplayName -eq '$($_.Name)'" -Properties $properties
if(-not $adUser) {
Write-Warning "'$($_.Name)' could not be found on AD"
return # Go next
}
$adUser
} | Select-Object $properties | Sort-Object Displayname | Format-Table

PowerShell Group-Object with dot notation

I'm trying to create one e-mail per manager, where the user accounts he is responsible for in active directory, will expire within a given time frame. It's already working quite well but I have some issue in grouping the Managers.
It would be nice if it was possible to group the managers together and then collect all the users where that specific manager is responsible for in a small HTML table ($Rows). The HTML-code is not the problem, but iterating the users for that manager is my issue.
The code:
$OU = 'OU=BBB,OU=EU,DC=domain,DC=net', 'OU=AAA,OU=EU,DC=domain,DC=net'
[INT]$Days = 30
$ExpUsers=$Objects=#()
Foreach ($O in $OU) {
$ExpUsers += Search-ADAccount -AccountExpiring -TimeSpan "$Days.00:00:00" -UsersOnly -SearchBase $O |
Select -ExpandProperty SamAccountName
}
Foreach ($E in $ExpUsers) {
$User = Get-ADUser $E -Properties * | Select SamAccountName, EmailAddress, GivenName,
SurName, AccountExpirationDate, Manager, DisplayName
$Manager = Get-ADUser $User.Manager -Properties * | Select SamAccountName, EmailAddress, GivenName, SurName
$Objects += [PSCustomObject]#{
User = $User
Manager = $Manager
}
}
$Objects | Group-Object Manager.SamAccountName | % {
$Rows=#()
foreach($M in $_) {
# Create HTML row for each user with the same manager
$M.Group.Manager.SurName
}
}
Grouping the managers can be done easily like this:
$Objects.Manager | Group-Object SamAccountName
However, when I do it like this I can't use the User properties anymore because they haven't been piped to Group-Object.
What is the best way to overcome this hurdle? I could of course create my object like this:
$Objects += [PSCustomObject]#{
UserSamAccountName = $User.SamAccountName
UserGivenName = $User.GivenName
ManagerSamAccountName = $Manager.SamAccountName
ManagerGivenName = $Manager.GivenName
}
But this solution doesn't seem to be so flexible if I want to add stuff later on.
I've found my answer in an example here:
$Objects | Group-Object {$_.Manager.SamAccountName} | % { $_.Group.User.GivenName}

powershell output of ad-user pwdLastSet and LastLoginTimeStamp

I'm working on a script that will run down a csv of LastName and FirstName of users on a domain and return someinfo about them in another csv.
Returning the properties is not an issue, but when I try to convert pwdLastSet and LastLogonTimeStamp to a readable format, it crashes when writing to the csv.
Here is my code. in this example, pwdLastSet will result in an unreadable 64bit number.
$names = import-csv C:\Users\me\Desktop\input.csv
$users = #()
foreach ($name in $names){
$filter = "givenName -like ""*$($name.FirstName)*"" -and sn -like ""$($name.LastName)"""
$users += get-aduser -filter $filter -Properties * | select-object employeeID, sn, givenName, distinguishedName, whencreated, passwordnotrequired, enabled, admincount, pwdlastset
}
$users | select employeeID, sn, givenName, distinguishedName, whencreated, passwordnotrequired, enabled, admincount, pwdlastset | export-csv c:\users\me\desktop\results.csv -NoTypeInformation
I'd like to throw $([datetime]::FromFileTime($user.pwdLastSet)) so it's readable in the output.
Any ideas?
You can use a calculated property to replace the value of a property or create an additional property:
$users += Get-ADUser -Filter $filter -Properties * |
select employeeID, ..., admincount,
#{n='pwdLastSet';e={[DateTime]::FromFileTime($_.pwdLastSet)}}
In this case it's an unnecessary step, though, because Get-ADUser already did that for you and placed that value in the PasswordLastSet property, as #user3815146 already pointed out (+1).
To get an overview of the properties of a user object you can use the Get-Member cmdlet:
Get-ADUser -Filter * -Property * | Get-Member
or list the members of any given user object:
Get-ADUser -Identity 'someuser' -Property * | Format-List *
On a more general note: never append to an array in a loop. The construct
$arr = #()
foreach ($item in $list) {
$arr += Do-StuffWith $item
}
guarantees poor performance, because with each iteration a new array is created (size + 1) and all elements are copied from the old array to the new one. Using a ForEach-Object loop in a pipeline provides far better performance:
$arr = $list | ForEach-Object { Do-StuffWith $_ }
Don't use pwdLastSet, try using PasswordLastSet.
On a different note, have you considered shortening your command to this:
$users += get-aduser -filter $filter -Properties employeeID,sn,givenName,distinguishedName, whencreated,passwordnotrequired,enabled,admincount,passwordlastset

How to get all groups that a user is a member of?

PowerShell's Get-ADGroupMember cmdlet returns members of a specific group. Is there a cmdlet or property to get all the groups that a particular user is a member of?
I fixed my mistake: Get-Member should be Get-ADGroupMember.
Get-ADPrincipalGroupMembership will do this.
Get-ADPrincipalGroupMembership username | select name
name
----
Domain Users
Domain Computers
Workstation Admins
Company Users
Company Developers
AutomatedProcessingTeam
Single line, no modules necessary, uses current logged user:
(New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User)(samAccountName=$($env:username)))")).FindOne().GetDirectoryEntry().memberOf
Kudos to this vbs/powershell article: http://technet.microsoft.com/en-us/library/ff730963.aspx
A more concise alternative to the one posted by Canoas, to get group membership for the currently-logged-on user.
I came across this method in this blog post: http://www.travisrunyard.com/2013/03/26/auto-create-outlook-mapi-user-profiles/
([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof
An even better version which uses a regex to strip the LDAP guff and leaves the group names only:
([ADSISEARCHER]"samaccountname=$($env:USERNAME)").Findone().Properties.memberof -replace '^CN=([^,]+).+$','$1'
More details about using the [ADSISEARCHER] type accelerator can be found on the scripting guy blog: http://blogs.technet.com/b/heyscriptingguy/archive/2010/08/24/use-the-powershell-adsisearcher-type-accelerator-to-search-active-directory.aspx
Old school way from CMD:
net user mst999 /domain
(GET-ADUSER –Identity USERNAME –Properties MemberOf | Select-Object MemberOf).MemberOf
This should provide you the details for current user. Powershell not needed.
whoami /groups
If you cannot get Get-ADPrincipalGroupMembership to work for you could try logging in as that user then use.
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
$groups = $id.Groups | foreach-object {$_.Translate([Security.Principal.NTAccount])}
$groups | select *
While there are many excellent answers here, there is one which I was personally looking for that was missing. Once I figured it out - I thought I should post it in case I want to find it later, or it actually manages to help someone else at some point:
Get-ADPrincipalGroupMembership username | Format-Table -auto
A second approach for presenting this is to specify the individual columns you are interested in eg:
Get-ADPrincipalGroupMembership username | select name, GroupScope, GroupCategory
This gives all the AD groups the username belongs to - but also presents all of the default properties of each group formatted nicely as a table.
The key benefit this gives you is you can see at a glance which are distribution lists, & which are Security groups. You can further see at a glance which are Universal, which are DomainLocal & which are Global.
Why would you care about this last bit?
Universal group is a security or distribution group that contains
users, groups, and computers from any domain in its forest as
members. You can give universal security groups rights and
permissions on resources in any domain in the forest.
Global group is a group that can be used in its own domain, in member
servers and in workstations of the domain, and in trusting domains.
In all those locations, you can give a global group rights and
permissions and the global group can become a member of local groups.
However, a global group can contain user accounts that are only from
its own domain.
Domain local group is a security or distribution group that can
contain universal groups, global groups, other domain local groups
from its own domain, and accounts from any domain in the forest. You
can give domain local security groups rights and permissions on
resources that reside only in the same domain where the domain local
group is located.
Get-Member is not for getting user's group membership. If you want to get a list of groups a user belongs to on the local system, you can do so by:
$query = "ASSOCIATORS OF {Win32_Account.Name='DemoUser1',Domain='DomainName'} WHERE ResultRole=GroupComponent ResultClass=Win32_Account"
Get-WMIObject -Query $query | Select Name
In the above query, replace DemoUser1 with the username you want and the DomainName with either your local computer name or domain name.
Get group membership for a user:
$strUserName = "Primoz"
$strUser = get-qaduser -SamAccountName $strUserName
$strUser.memberof
See Get Group Membership for a User
But also see Quest's Free PowerShell Commands for Active Directory.
[Edit: Get-ADPrincipalGroupMembership command is included in Powershell since v2 with Windows 2008 R2. See kstrauss' answer below.]
Get-Member is a cmdlet for listing the members of a .NET object. This has nothing to do with user/group membership. You can get the current user's group membership like so:
PS> [System.Security.Principal.WindowsIdentity]::GetCurrent().Groups |
Format-Table -auto
BinaryLength AccountDomainSid Value
------------ ---------------- -----
28 S-1-5-21-... S-1-5-21-2229937839-1383249143-3977914998-513
12 S-1-1-0
28 S-1-5-21-... S-1-5-21-2229937839-1383249143-3977914998-1010
28 S-1-5-21-... S-1-5-21-2229937839-1383249143-3977914998-1003
16 S-1-5-32-545
...
If you need access to arbitrary users' group info then #tiagoinu suggestion of using the Quest AD cmdlets is a better way to go.
I wrote a PowerShell function called Get-ADPrincipalGroupMembershipRecursive. It accepts the DSN of a user, computer, group, or service account. It retrieves an initial list of groups from the account's memberOf attribute, then recursively checks those group's memberships. Abbreviated code is below. Full source code with comments can be found here.
function Get-ADPrincipalGroupMembershipRecursive( ) {
Param(
[string] $dsn,
[array]$groups = #()
)
$obj = Get-ADObject $dsn -Properties memberOf
foreach( $groupDsn in $obj.memberOf ) {
$tmpGrp = Get-ADObject $groupDsn -Properties memberOf
if( ($groups | where { $_.DistinguishedName -eq $groupDsn }).Count -eq 0 ) {
$groups += $tmpGrp
$groups = Get-ADPrincipalGroupMembershipRecursive $groupDsn $groups
}
}
return $groups
}
# Simple Example of how to use the function
$username = Read-Host -Prompt "Enter a username"
$groups = Get-ADPrincipalGroupMembershipRecursive (Get-ADUser $username).DistinguishedName
$groups | Sort-Object -Property name | Format-Table
No need for long scripts when it is a simple one liner..
QUEST Command
(Get-QADUser -Identity john -IncludedProperties MemberOf | Select-Object MemberOf).MemberOf
MS AD Command
(GET-ADUSER –Identity john –Properties MemberOf | Select-Object MemberOf).MemberOf
I find the MS AD cmd is faster but some people like the Quest ones better..
Steve
Use:
Get-ADPrincipalGroupMembership username | select name | export-CSV username.csv
This pipes output of the command into a CSV file.
First, import the ActiveDirectory module:
Import-Module ActiveDirectory
Then issue this command:
Get-ADGroupMember -Identity $group | foreach-object {
Write-Host $_.SamAccountName
}
This will display the members of the specified group.
It is just one line:
(get-aduser joe.bloggs -properties *).memberof
end of :)
The below works well:
get-aduser $username -Properties memberof | select -expand memberof
If you have a list of users:
$list = 'administrator','testuser1','testuser2'
$list | `
%{
$user = $_;
get-aduser $user -Properties memberof | `
select -expand memberof | `
%{new-object PSObject -property #{User=$user;Group=$_;}} `
}
Get-QADUser -SamAccountName LoginID | % {$_.MemberOf } | Get-QADGroup | select name
Get-ADUser -Filter { memberOf -RecursiveMatch "CN=Administrators,CN=Builtin,DC=Fabrikam,DC=com" } -SearchBase "CN=Administrator,CN=Users,DC=Fabrikam,DC=com" -SearchScope Base
## NOTE: The above command will return the user object (Administrator in this case) if it finds a match recursively in memberOf attribute.
I couldn't get the following to work for a particular user:
Get-ADPrincipalGroupMembership username
It threw an error that I was not willing to troubleshoot.
I did however come up with a different solution using Get-ADUser. I like it a bit better because if you don't know the account name then you can get it based off of a wildcard on the user's actual name. Just fill in PartOfUsersName and away it goes.
#Get the groups that list of users are the member of using a wildcard search
[string]$UserNameLike = "*PartOfUsersName*" #Use * for wildcards here
[array]$AccountNames = $(Get-ADUser -Filter {Name -like $UserNameLike}).SamAccountName
ForEach ($AccountName In $AccountNames) {
Write-Host "`nGETTING GROUPS FOR" $AccountName.ToUpper() ":"
(Get-ADUser -Identity $AccountName -Properties MemberOf|select MemberOf).MemberOf|
Get-ADGroup|select Name|sort name
}
Huge props to schmeckendeugler and 8DH for getting me to this solution. +1 to both of you.
To get it recursive, you can use:
<#
.SYNOPSIS
Get all the groups that a user is MemberOf.
.DESCRIPTION
This script retrieves all the groups that a user is MemberOf in a recursive way.
.PARAMETER SamAccountName
The name of the user you want to check #>
Param (
[String]$SamAccountName = 'test',
$DomainUsersGroup = 'CN=Domain Users,CN=Users,DC=domain,DC=net'
)
Function Get-ADMemberOf {
Param (
[Parameter(ValueFromPipeline)]
[PSObject[]]$Group,
[String]$DomainUsersGroup = 'CN=Domain Users,CN=Users,DC=grouphc,DC=net'
)
Process {
foreach ($G in $Group) {
$G | Get-ADGroup | Select -ExpandProperty Name
Get-ADGroup $G -Properties MemberOf| Select-Object Memberof | ForEach-Object {
Get-ADMemberOf $_.Memberof
}
}
}
}
$Groups = Get-ADUser $SamAccountName -Properties MemberOf | Select-Object -ExpandProperty MemberOf
$Groups += $DomainUsersGroup
$Groups | Get-ADMemberOf | Select -Unique | Sort-Object
Studying all comments presented gave me a starting point (thanks for such) but left me with several unresolved issues. As result here is my answer. The code snippet provided does a little more than what is asked for but it provides helpful debugging info.
[array] $script:groupsdns = #()
function Get-ADPrincipalGroupMembershipRecursive()
{
Param( [string] $dn, [int] $level = 0, [array] $groups = #() )
#if(($groupsdns | where { $_.DistinguishedName -eq $dn }).Count -ne 0 ) { return $groups } # dependency on next statement
#$groupsdns += (Get-ADObject $dn -Properties MemberOf) # Get-ADObject cannot find an object with identity
if ($script:groupsdns.Contains($dn)) { return $groups }
$script:groupsdns += $dn
$mo = $Null
$mo = Get-ADObject $dn -Properties MemberOf # Get-ADObject cannot find an object with identity
$group = ($dn + " (" + $level.ToString())
if ($mo -eq $Null) { $group += "!" }
$group += ")"
$groups += $group
foreach( $groupdn in $mo.MemberOf )
{
$groups = Get-ADPrincipalGroupMembershipRecursive -dn $groupdn -level ($level+1) -groups $groups
}
if ($level -le 0)
{
$primarygroupdn = (Get-ADUser -Identity $dn -Properties PrimaryGroup).PrimaryGroup
$groups = Get-ADPrincipalGroupMembershipRecursive -dn $primarygroupdn -level ($level+1) -groups $groups
}
return $groups
}
$adusergroups = Get-ADPrincipalGroupMembershipRecursive -dn $aduser.DistinguishedName
$adusergroups | ft -AutoSize | `
Out-File -Width 512 Get-ADPrincipalGroupMembershipRecursive.txt #-Append #-Wrap # | Sort-Object -Property Name
When you do not have privileges to consult other member groups but you do have the privilege to consult group members, you can do the following to build a map of which user has access to which groups.
$groups = get-adgroup -Filter * | sort name | select Name
$users = #{}
foreach($group in $groups) {
$groupUsers = #()
$groupUsers = Get-ADGroupMember -Identity $group.Name | Select-Object SamAccountName
$groupUsers | % {
if(!$users.ContainsKey($_.SamAccountName)){
$users[$_.SamAccountName] = #()
}
($users[$_.SamAccountName]) += ($group.Name)
}
}
For LOCAL users and groups (ie not in Active Directory), and if you don't want to, or aren't allowed to, or can't install RSAT and/or Install-WindowsFeature RSAT-AD-PowerShell and/or import-module activedirectory then here's a pure, pre-installed powershell (5.1+) way to do it.
(Note: Get-LocalGroup* used below are only available Powershell v5.1 and above. "...v5.1 was released along with the Windows 10 Anniversary Update on August 2, 2016, and in Windows Server 2016. ...[F]or Windows 7, Windows Server 2008, Windows Server 2008 R2, Windows Server 2012, and Windows Server 2012 R2 [it] was released on January 19, 2017." (wikipedia))
$username = "user002"
Get-LocalGroup | ForEach-Object {
# the usernames are returned in the string form "computername\username"
if (Get-LocalGroupMember -Group $_ | Where-Object name -like "*\$username") {
$_.name
}
}
Example output:
Administrators
Users
Import-Module ActiveDirectory
Get-ADUser -SearchBase "OU=Users,DC=domain,DC=local" -Filter * | foreach-object {
write-host "User:" $_.Name -foreground green
Get-ADPrincipalGroupMembership $_.SamAccountName | foreach-object {
write-host "Member Of:" $_.name
}
}
Change the value of -SearchBase to reflect the OU you need to list the users from :)
This will list all of the users in that OU and show you which groups they are a member of.
Get-ADPrincipalGroupMembership USERLOGON | select name
This is the simplest way to just get the names:
Get-ADPrincipalGroupMembership "YourUserName"
# Returns
distinguishedName : CN=users,OU=test,DC=SomeWhere
GroupCategory : Security
GroupScope : Global
name : testGroup
objectClass : group
objectGUID : 2130ed49-24c4-4a17-88e6-dd4477d15a4c
SamAccountName : testGroup
SID : S-1-5-21-2114067515-1964795913-1973001494-71628
Add a select statement to trim the response or to get every user in an OU every group they are a user of:
foreach ($user in (get-aduser -SearchScope Subtree -SearchBase $oupath -filter * -Properties samaccountName, MemberOf | select samaccountName)){
Get-ADPrincipalGroupMembership $user.samaccountName | select name}
Almost all above solutions used the ActiveDirecotry module which might not be available by default in most cases.
I used below method. A bit indirect, but served my purpose.
List all available groups
Get-WmiObject -Class Win32_Group
And then list the groups the user belongs to
[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups
Comparison can then be done via checking through the SIDs. This works for the logged in user. Please correct me if I am wrong. Completely new to PowerShell, but had to get this done for a work commitment.
With user input and fancy output formatting:
[CmdletBinding(SupportsShouldProcess=$True)]
Param(
[Parameter(Mandatory = $True)]
[String]$UserName
)
Import-Module ActiveDirectory
If ($UserName) {
$UserName = $UserName.ToUpper().Trim()
$Res = (Get-ADPrincipalGroupMembership $UserName | Measure-Object).Count
If ($Res -GT 0) {
Write-Output "`n"
Write-Output "$UserName AD Group Membership:"
Write-Output "==========================================================="
Get-ADPrincipalGroupMembership $UserName | Select-Object -Property Name, GroupScope, GroupCategory | Sort-Object -Property Name | FT -A
}
}
Putting this here for future reference. I'm in the midst of an email migration. I need to know each user account and its respective group membership, and also I need to know each group and its respective members.
I'm using the code block below to output a CSV for each user's group membership.
Get-ADUser -Filter * |`
ForEach-Object { `
$FileName = $_.SamAccountName + ".csv" ; `
$FileName ; `
Get-ADPrincipalGroupMembership $_ | `
Select-Object -Property SamAccountName, name, GroupScope, GroupCategory | `
Sort-Object -Property SamAccountName | `
Export-Csv -Path $FileName -Encoding ASCII ; `
}
The export process for the groups and their respective members was a little convoluted, but the below works. The output filenames include the type of group. Therefore, the email distribution groups I need are/should be the Universal and Global Distribution groups. I should be able to just delete or move the resulting TXT files I don't need.
Get-ADGroup -Filter * | `
Select-Object -Property Name, DistinguishedName, GroupScope, GroupCategory | `
Sort-Object -Property GroupScope, GroupCategory, Name | `
Export-Csv -Path ADGroupsNew.csv -Encoding ASCII
$MyCSV = Import-Csv -Path .\ADGroupsNew.csv -Encoding ASCII
$MyCSV | `
ForEach-Object { `
$FN = $_.GroupScope + ", " + $_.GroupCategory + ", " + $_.Name + ".txt" ; `
$FN ; `
Get-ADGroupMember -Identity $_.DistinguishedName | `
Out-File -FilePath $FN -Encoding ASCII ; $FN=""; `
}