Get-ADUser not returning MemberOf for some users

Answered Get-ADUser not returning MemberOf for some users

  • Wednesday, March 14, 2012 1:51 PM
     
     

    I am using a basic powershell query to obtain a user's group membership:

    (Get-ADUser -Identity JohnB -Properties memberof | Select-Object MemberOf).memberof

    This method works great for most users, but for some user accounts the property; "MemberOf" is empty. If I view the the same user's groups in ADUC I can see the user is a member of multiple groups. I am trying to figure out why the groups are not being displayed using the powershell query.

    I am looking to either use a different method in powershell or trying to figure out why the current query is not working. I am also not looking to use the Quest PowerShell QAD commands.

All Replies

  • Wednesday, March 14, 2012 3:37 PM
    Moderator
     
     

    Hi,

    There are two complications that come to mind. First, the primary group is not listed in the memberof attribute. Second, if the user object's group membership has changed but AD replication hasn't completed, it can affect the query's results (depending on which DC returns results).

    Bill

  • Wednesday, March 14, 2012 3:41 PM
    Moderator
     
     Proposed Has Code

    The statement works fine for me. Of course, it will never reveal membership in the "primary" group (usually "Domain Users"). For me it worked whether memberOf had no entries, one entry, or more than one. Do you get an error message? Are you identifying the users by "pre-Windows 2000 logon" name on the "Account" tab of ADUC?

    You can also compare to:

    dsquery user -samid JohnB | dsget user -memberOf

    which should give the same result, except it includes the "primary" group.


    Richard Mueller - MVP Directory Services

    • Proposed As Answer by Shabarinath Wednesday, March 14, 2012 8:56 PM
    •  
  • Wednesday, March 14, 2012 3:51 PM
     
     
    The group membership has not changed so replication is not an issue. I'm not sure what you mean by the primary group, I have similar accounts which have the same permissions but seem to display the permissions fine using the powershell command:

    (Get-ADUser -Identity JohnB -Properties memberof | Select-Object MemberOf).memberof

    The real issue i'm trying to solve is why one account is displaying the "MemberOf" property and the other is not.

  • Wednesday, March 14, 2012 4:04 PM
    Moderator
     
      Has Code

    Hi,

    I can't reproduce your problem either. The primary group is described here:

    http://technet.microsoft.com/en-us/library/cc776334.aspx

    In any case I don't think you have a scripting issue. Try the command Richard suggested:

    dsquery user -samid JohnB | dsget user -memberof

    It should produce the same output as the PowerShell AD module.

    Bill

  • Wednesday, March 14, 2012 4:08 PM
     
     

    I'm using the user logon name and or the pre-Windows login name, they are both the same.

    I tried your command which only "gives me the Domain Users"
    dsquery user -samid JohnB | dsget user -memberOf

    Any other ideas?

  • Wednesday, March 14, 2012 4:16 PM
     
     
    There seems to be something strange with these accounts, sometime i can get a full output and other times I am not able to get anything using the same code? Any more ideas?
  • Wednesday, March 14, 2012 4:30 PM
    Moderator
     
     

    There is nothing wrong with the code. The group "Domain Users" is the "primary" group of the user. You can check this in ADUC on the "MemberOf" tab of user properties in ADUC (near the bottom). If a user is member only of "Domain Users", the memberOf collection will be empty. This is expected. Best practice is to never modify "primary" group membership and simply assume that all users are member of this group.

    The dsquery/dsget command should always agree, except the PowerShell script (and any method that enumerates the memberOf attribute) will not include "Domain Users". Does this explain what you see.


    Richard Mueller - MVP Directory Services

  • Wednesday, March 14, 2012 4:35 PM
     
     
    I understand what a primary group is at this point and how the two commands differ. I'm ok with the primary group not being displayed, but the rest of the groups are not being displayed either and they are not primary groups.
  • Wednesday, March 14, 2012 5:36 PM
    Moderator
     
     

    Does the dsquery/dsget command also not reveal the other group memberships? Are there any unusual characters in the missing group DN's? Are all of the groups in the same domain? We cannot duplicate the problem, so we are guessing. However, I'd like to understand why the cmdlet seems to fail. I guess another possibility is that you aren't permitted to read the memberOf attribute, but then I would expect the same problem in ADUC. Finally, are there any unusual characters the DN of the user? For example, is there an asterick character, "*"?


    Richard Mueller - MVP Directory Services

  • Wednesday, March 14, 2012 8:21 PM
     
      Has Code

    i can send you the entire script this way you can see the entire scope:

    Import-Module ActiveDirectory
    # Main Menu
    Write-Host "Welcome to Member of Whaaaaaat?" -foregroundcolor "yellow"
    Write-Host `
    "***************************************** `
    *                                       * `
    *    Use this utility to gather group   * `
    *    membership for Users and Groups.   * `
    *                                       * `
    *****************************************" -foregroundcolor "Red"
    Write-Host "1. Search Groups"
    Write-Host "2. Search Users"
    do {
      $a = read-host "Please select a search type"
    }
    until (($a -eq "1") -or ($a -eq "2"))
    Write-Host " "
    switch ($a) 
        { 
            1 {
               $Choice = "1"
              } 
            2 {
               $Choice = "2"
              } 
    }
    $smtpServer = "relay.abc.com"
    Function SendMail
            {	
            	$msg = new-object Net.Mail.MailMessage
            	$smtp = new-object Net.Mail.SmtpClient($smtpServer)
                $att = new-object Net.Mail.Attachment($CsvFile)
            	$msg.From = $From
            	$msg.To.Add($To)
            	#$msg.bcc.Add($BccAddress)
            	$msg.Subject = ($Subject)
            	$msg.Body = $MessageBody
            	$msg.IsBodyHTML = $true
                $msg.Attachments.Add($att)
            	$smtp.Send($msg)
            }
    IF ($Choice -eq "1")
    {
    Clear-Host
    DO 
    {
    (Write-Host `
    "***************************************** `
    *    This script will only return user  * `
    *    objects. Nested groups will not be * ` 
    *    displayed. Group Names should be   * `
    *    entered with their 'Group Name',   * `
    *    not an Exchange Alias.             * `
    *****************************************" -foregroundcolor "Red")
    $GroupName = Read-Host "Please enter a Active Directory Group"
    }
    Until (Get-ADGroup -filter {SamAccountName -eq $GroupName})
    Clear-Host
    (Write-Host `
    "***************************************** `
    *    This script will only return user  * `
    *    objects. Nested groups will not be * ` 
    *    displayed. Group Names should be   * `
    *    entered with their 'Group Name',   * `
    *    not an Exchange Alias.             * `
    *****************************************" -foregroundcolor "Red")
    Write-Host AD Group Name:$GroupName -foregroundcolor "yellow"
    $From = "dontreply@na.abc.com"
    $To = Read-Host "Please enter the email address of the recipient of the .csv file"
    Clear-Host $To
    Write-Host AD Group Name:$GroupName -foregroundcolor "yellow"
    Write-Host E-Mail sent to:$To -foregroundcolor "yellow"
    $Subject = "Group Membership Request: $GroupName"
    $CsvFile = "C:\temp\$GroupName.csv"
    ####
    ## Email Body
    ####
    $MessageBody = $MessageBody + @"
    <body>
    <p>Your group membership request for:
    <p><b>$GroupName</b>
    <p>is attached in a .csv formatted document.
    </p>
    </body></html>
    "@
    $ColItems = Get-ADGroupMember -Identity $GroupName
    $GroupItems = @()
    FOREACH ($Colitem in $Colitems)
    {
        IF ($Colitem.ObjectClass -eq "user")
        {
            $SamAccountName = $ColItem.samaccountname
            $UserData = get-aduser -Identity $SamAccountName -properties displayname,samaccountname,department,lastlogondate
            $GrpItems = New-Object -TypeName PSObject -Property @{
            DisplayName = $UserData.displayname
            SamName = $UserData.SamAccountName
            Department = $UserData.Department
            LastLogon = $UserData.LastLogonDate
            }
        $GroupItems += $GrpItems 
        }       
    }
    $GroupItems | Select-Object DisplayName,SamName,Department,LastLogon | Sort-Object -property DisplayName | export-csv $CsvFile -NoTypeInformation
    SendMail
    }
    ELSEIF ($Choice -eq "2")
    {
    Clear-Host
    DO 
    {
    (Write-Host `
    "******************************************** `
    *   This script will return a User's group  * `
    *   memberships. User names should be       * `
    *   entered using the SamAccountName, not   * `
    *   an Exchange Alias.                      * `
    ********************************************" -foregroundcolor "Red")
    $UserName = Read-Host "Please enter an Active Directory User Name"
    }
    Until (Get-ADuser -filter {SamAccountName -eq $UserName})
    Clear-Host
    (Write-Host `
    "******************************************** `
    *   This script will return a User's group  * `
    *   memberships. User names should be       * `
    *   entered using the SamAccountName, not   * `
    *   an Exchange Alias.                      * `
    ********************************************" -foregroundcolor "Red")
    Write-Host AD User Name:$UserName -foregroundcolor "yellow"
    $From = "dontreply@na.abc.com"
    $To = Read-Host "Please enter the email address of the recipient of the .csv file"
    Clear-Host $To
    Write-Host AD User Name:$UserName -foregroundcolor "yellow"
    Write-Host E-Mail sent to:$To -foregroundcolor "yellow"
    $Subject = "User Group Membership Request: $UserName"
    $CsvFile = "C:\temp\$UserName.csv"
    ####
    ## Email Body
    ####
    $MessageBody = $MessageBody + @"
    <body>
    <p>Your user membership request for:
    <p><b>$UserName</b>
    <p>is attached in a .csv formatted document.
    </p>
    </body></html>
    "@
    $Groups = (Get-ADUser -Identity $UserName -Properties memberof | Select-Object MemberOf).memberof
    #$Groups = dsquery user -samid $UserName | dsget user -memberof
    $GroupItems = @()
    FOREACH ($Group in $Groups)
    {
      $var = $group.split(",")
      $var1 = $var[0]
      $ADGroup = $var1.Substring(3)  
      $GrpItems = New-Object -TypeName PSObject -Property @{
      Memberof = $ADGroup}
      
      $GroupItems += $GrpItems
    }
    Write-Host $GroupItems 
              
    }
    $GroupItems | Sort-Object -property Memberof | export-csv $CsvFile -NoTypeInformation
    SendMail
    BREAK
    {
    }#Exit

  • Wednesday, March 14, 2012 9:58 PM
    Moderator
     
     

    The script you posted boils down to the PowerShell statement you've already reported (for choice 2, to enumerate the membership if a user specified by sAMAccountName). I see nothing wrong with it, and I cannot duplicate the problem. You parse the group DN's for the common name, under the assumption that the name will not contain any commas. Even in the unusual case where the value of the cn attribute does contain a comma, you still should see a partial name (unless the comma is the first character in the common name).


    Richard Mueller - MVP Directory Services

  • Wednesday, March 14, 2012 10:07 PM
     
     
    I guess if we know the script is good, i really have to figure out why some accounts return the groups and others do not. I am going to try to copy one of the accounts that is not working and see if it experiences the same result.
  • Wednesday, March 14, 2012 11:41 PM
     
      Has Code

    I have extracted a bit of your code and removed all of the unnecessary bits so that you have oly a simple function that returns the required data.

    Try calling this function with a group name and see what it returns.

    The function will dump the CSVfile to the console for validation.

    function SearchGroups($groupname){
        $To = Read-Host "Please enter the email address of the recipient of the .csv file"
        Write-Host E-Mail sent to:$To -foregroundcolor "yellow"
        $CsvFile = "C:\temp\$GroupName.csv"
        $ColItems = Get-ADGroupMember -Identity $groupname
        $GroupItems=foreach($item in $Colitems){
            if($item.ObjectClass -eq 'user'){
                $user=get-aduser -Identity $ColItem.samaccountname -properties displayname,samaccountname,department,lastlogondate
            }       
        }
        $GroupItems | Sort-Object -property DisplayName | export-csv $CsvFile -NoTypeInformation
        cat $CsvFile
        #Send-MailMessage -To $To -From "dontreply@na.abc.com" -subject "Group Membership Request: $GroupName" -attachments $CsvFile
    }

    You shuold do teh same for the user search,.  By encapsulating this into a function vlock it is esier to debug.  Once the function block is working  it can be easily added into the whole script.

    My susupicions are that this script of yours has a number of hard to see logic errors.  By breaking it down into simpler pieces you will be able to find the errors.


    ¯\_(ツ)_/¯

  • Thursday, March 15, 2012 1:45 AM
    Moderator
     
      Has Code

    jv, I think that is the code for "Choice 1". The script is not easy to read, but I believe the problem is with the code for "Choice 2" farther down. The relevant part, which I tested and found to work for me, is:

    $UserName = "JohnB"
    $Groups = (Get-ADUser -Identity $UserName -Properties memberof | Select-Object MemberOf).memberof
    $GroupItems = @()
    FOREACH ($Group in $Groups)
    {
      $var = $group.split(",")
      $var1 = $var[0]
      $ADGroup = $var1.Substring(3)  
      $GrpItems = New-Object -TypeName PSObject -Property @{
      Memberof = $ADGroup}
      
      $GroupItems += $GrpItems
    }
    Write-Host $GroupItems

    -----



    Richard Mueller - MVP Directory Services

  • Thursday, March 15, 2012 2:29 AM
     
     
    The issue is in choice two you are correct. Could it be that i have reused $var and $var1 throughout the script instead of using different variables? The strange part is sometimes for the user in question I do get the output that I am looking for, its as if there is something in the environment that is changing? FYI am new to scripting and powershell so I am learning as I go.
  • Thursday, March 15, 2012 2:51 AM
     
     
    The issue is in choice two you are correct. Could it be that i have reused $var and $var1 throughout the script instead of using different variables? The strange part is sometimes for the user in question I do get the output that I am looking for, its as if there is something in the environment that is changing? FYI am new to scripting and powershell so I am learning as I go.

    Your script is on the edge of what we call spagheti code. You need to spend some time structuring it more.

    The problem is in the code and is because the paths through the code are ambiguous.  I have tried to re-write (refactor) the code but have found the logic almost impossible to follow at all steps.

    Simplify and test and you will find the problem,


    ¯\_(ツ)_/¯


  • Thursday, March 15, 2012 1:36 PM
     
      Has Code

    JVR,

    I took your advice on breaking the code out into a function to debug it. I seem to be able to get the information that I am looking for to the screen but i am not able to export it to a .csv and I am getting an error that I am not sure how to resolve. Any help would be apprciated.

    Error and Question:

    Code:

    Import-Module Activedirectory
    $User = $args[0]
    Function GetADUser{
        $ADUsers = dsquery user -samid $User | dsget user -memberof
    					$ADGroupItems = @()
    			FOREACH ($ADUser in $ADUsers)
    				{
    					$DN = $ADUser.split(",")
    					$CN = $DN[0]
    					$GrpFriendlyName = $CN.Substring(4)
                        Write-Host $GrpFriendlyName
                        $GrpItems = New-Object -TypeName PSObject -Property @{
                        Memberof = $GrpFriendlyName}
    					$ADGroupItems += $GrpItems
                        
    				}	
    }
    GetADUser $User
    $ADGroupItems | Sort-Object -property Memberof | export-csv "C:\temp\functiontest.csv" -NoTypeInformation

  • Thursday, March 15, 2012 2:07 PM
    Moderator
     
     Answered Has Code

    First, you are now using dsquery and dsget (instead of Get-ADUser). Second the dsquery/dsget returns a blank line, that raises the subscript error. Finally, you need to take the output of the function and redirect it to the csv file. The following version of your PowerShell script worked for me:

    $User = $args[0]
    Function GetADUser{
        $ADUsers = dsquery user -samid $User | dsget user -memberof
        $ADGroupItems = @()
        FOREACH ($ADUser in $ADUsers)
        {
            If ($ADUser.Length -gt 3)
            {
                $DN = $ADUser.split(",")
                $CN = $DN[0]
                $GrpFriendlyName = $CN.Substring(4)
                Write-Host $GrpFriendlyName
                $GrpItems = New-Object -TypeName PSObject -Property @{
                Memberof = $GrpFriendlyName}
                $ADGroupItems += $GrpItems
            }
        }    
        $ADGroupItems
    }
    GetADUser $User | Sort-Object -property Memberof | export-csv "C:\temp\functiontest.csv" -NoTypeInformation

    -----



    Richard Mueller - MVP Directory Services

    • Marked As Answer by CMR NYC Thursday, March 15, 2012 2:22 PM
    •  
  • Thursday, March 15, 2012 2:17 PM
    Moderator
     
      Has Code

    This version of your program using Get-ADUser also works for me (although it does not include "Domain Users"):

    $User = $args[0]
    Function GetADUser{
        $ADUsers = (Get-ADUser -Identity $User -Properties memberof | Select-Object MemberOf).memberof
        $ADGroupItems = @()
        FOREACH ($ADUser in $ADUsers)
        {
            $DN = $ADUser.split(",")
            $CN = $DN[0]
            $GrpFriendlyName = $CN.Substring(3)
            Write-Host $GrpFriendlyName
            $GrpItems = New-Object -TypeName PSObject -Property @{
            Memberof = $GrpFriendlyName}
            $ADGroupItems += $GrpItems
        }    
        $ADGroupItems
    }
    GetADUser $User | Sort-Object -property Memberof | export-csv "C:\temp\functiontest.csv" -NoTypeInformation

    -----

    Now there is no need to skip blank lines, and the values retrieved by Get-ADUser are not quoted.


    Richard Mueller - MVP Directory Services


  • Thursday, March 15, 2012 2:18 PM
     
      Has Code

    YOu need to step back and think about what you are trying to do.

    You are asking for a user object by samid.  YOu are tehn trying to get teh groups the user is a member of.  "memeberOf" is a collection of 1 or more group distinguished names.

    Whty not save yourself a lot of trouble and just put teh dns in teh mail message body instead of trying to put them in a CSV.  I can see no use for teh CSV bit.  Yu cannot import it or do much of anything with it.  The fn ccan be used to add a user ot a group or remove them from a group.

    YOur code says; "foreach( $aduser in $ADUsers) which is completely wrong from the nomenclature perspective.  What dsget has returned is a colelction of DNs.

    Run the line and see what you get: ( replace $user with a real samid

    dsquery user -samid $User | dsget user -memberof

    You can also do this easily.

    $groups=dsquery user -samid $User | dsget user -memberof
    $groups | sort

    Make it work at the commandline and the rest will come easier.

    If you really want to play with objects here is an example of how they work.

    $groups | 
         ForEach-Object{
              $GrpFriendlyName = ($_.split(",")[0]).Split('=')[1]
              Write-Host $GrpFriendlyName -fore green
              New-Object -TypeName PSObject -Property @{
                   Memberof = $GrpFriendlyName
                   DistinguishedName=$_
              }
         }



    ¯\_(ツ)_/¯

  • Thursday, March 15, 2012 2:21 PM
     
     

    Richard - don't you think that all of the reformating and recollecting is just unnecessary.

    What is wrong with just putting the DNs into the mail message.  Extracting the CN and putting it in a CSV file is kind of odd.  What pourpose is the CSV file?


    ¯\_(ツ)_/¯

  • Thursday, March 15, 2012 2:22 PM
     
     Answered
    OK, That worked perfect.. Just so i understand what is going on, in the "If ($ADUser.Length -gt 3)", this statement sayd we are going to process anything that is larger than three characters. What item was causing the issue that was less than three? Is this skipping a group? One other thing, witht he .csv file, if i keep the PS window open the file remains locked, is there a way to release the file lock when the script ends?
    • Marked As Answer by CMR NYC Thursday, March 15, 2012 2:22 PM
    •  
  • Thursday, March 15, 2012 2:31 PM
     
     

    I have run many manmy dsget -memberof queries.  I have never seen a blank line.  How are you getting a blank line?


    ¯\_(ツ)_/¯

  • Thursday, March 15, 2012 4:26 PM
    Moderator
     
      Has Code

    The dsquery / dsget statements output a blank line at the end that causes the subscript out of range error. I got the same error when I tested. I avoided the error by skipping blank lines, which I did by requiring that all lines be more than 3 characters long (each should start with "CN="). This doesn't happen when you use Get-ADUser.

    I agree that the extra code to parse for the common names of the groups is not useful. I would email the group distinguished names. Worse, the code that parses for the common names will fail if the group name has an embedded comma. This should be rare, but can happen (it happens with user objects all the time). In similar situations (where the person insists on the common name), I use code similar to below to handle commas in the common name:

    $CN = ($ADUser -Replace "\\,", "###" -Split ",")[0] -Replace "###", ","

    -----

    It took awhile to realize I had to escape the backslash escape character when I replaced "\," with "###" above. I decided not to escape the comma in the output, since it confuses people.

    Of course, the other issue is that the common name does not uniquely identify the object. I worked for a large electric utility, and each of the 16 power plants had groups with names "Admin", "Operating", and "Maintenance". It would be silly for them not to standardize their group names, but it meant there were 16 groups with common name "Admin" (at least). Of course, each must have a different sAMAccountName (pre-Windows 2000 name). If you don't use the DN to identify the objects, the best procedure is to obtain a reference to the AD object and retrieve either cn or sAMAccountName, rather than attempting to parse the DN.


    Richard Mueller - MVP Directory Services

  • Thursday, March 15, 2012 9:15 PM
     
     
     

    Oh - the last line is blank.

    This is how I fix that issue globally:

    $x=(dsquery user -samid $user| dsget user -memberof)
    $groups=$x[0..($x.count-2)]

    This extracts all but the last line.


    ¯\_(ツ)_/¯

  • Friday, March 30, 2012 3:49 AM
     
     

    Try this

    $bob = get-aduser bobsmith -Properties *
    $bob.memberof