Add Content formatting is wrapping the text.

This topic contains 8 replies, has 3 voices, and was last updated by  Dave Wyatt 4 years ago.

  • Author
    Posts
  • #9752

    notarat
    Participant

    I have a script that looks through an OU and lists all Security Groups, and the total members of each.

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'OU to search here'
        foreach ($group in $groups) {
        $groupname=($group.name)
        $membercount=@(Get-ADGroupMember $groupname).count
        $strdone = ($group.name),',',$membercount
        Add-content c:\outputfiles\CountofUsersinEachGroup.txt -value $strdone
    }

    The output should look like:
    SecGroup1,13
    SecGroup2,1
    SecGroup3,11
    SecGroup4,4
    SecGroup5,5

    But instead, it looks like the follwing:
    SecGroup1
    ,
    13
    SecGroup2
    ,
    1
    SecGroup3
    ,
    11
    SecGroup4
    ,
    4

    I've used Add-Content many times in other scripts in the past and this is the first time it is "wrapping" to a new line.

    My other scripts that use Add-Content are working fine (I checked today) so I'm not sure why the formatting is "off" on this one script.

    If someone could let me know what I missed I'd appreciate it.

  • #9753

    Richard Siddaway
    Moderator

    Its this line
    $strdone = ($group.name),',',$membercount

    that's causing the problem. You need to concatenate the two strings so either use

    $strdone = ($group.name) + ',' + $membercount

    OR
    $strdone = "$($group.name),$membercount"

    A comma won't concatenate you strings. The data is being treated as an array of strings which why you are getting the data split across multiple lines

  • #9754

    notarat
    Participant

    Bah! /me has teh dumb!

    It was staring me in the face all morning! I was looking through my other scripts and they are all like your example

    $strdone = ($blah) +','+ ($blah1) and I completely overlooked the + signs,lol

  • #9757

    notarat
    Participant

    Okay I tried some changes suggested by others and came up with the following (99% not my code because a poster explained how to use $props via the example code below)

    $groups = Get-ADGroup -f * -searchbase 'My OU Here'
    foreach ($group in $groups) {
    $members=Get-ADGroupMember $groupname | Get-ADUser -Property *
    $props=@{
    	GroupName=$group.name
    	MemberCount=$members.count
    	InactiveCount=@($members|?{$_.lockedout -or $_.Enabled -eq $false}).count
    	}
    New-Object PsObject -Property $props | Format-Table -AutoSize
    }

    When I run this, I get only about 1/10th of the group information. The rest (vast majority) throw up errors during the Get-ADGroupmember or Get-ADUser section like the following:

    (from the Get-ADGroupmember command) + $members=Get-ADGroupMember $groupname | Get-ADUser < <<< -Property * + CategoryInfo : ResourceUnavailable: (CN=john.q.pub...,DC=company,DC=com:ADUser) [Get-ADUser], ADReferralException (from the Get-ADUser command) : ResourceUnavailable: (CN=norman.prese...,DC=com,DC=:ADUser) I'm not sure why the errors appear because when I execute the commands manually, I get good data

    $m=Get-ADGroupMember SecurityGroupName  | Get-ADUser -Property *
    $m | select Name (will list all 47 members of group)
    $i=($m|?{$_.lockedout -or $_.enabled -like 'false'}).count
    PS F:\> $i
    PS F:\> 27 (this is how many accts in this group are disabled or locked out)
    PS F:\> $m.count
    PS F:\> 47 (this is how many total accts are in this group)

  • #9758

    Richard Siddaway
    Moderator

    Your logic isn't identical in the script and manual steps

    script:
    InactiveCount=@($members|?{$_.lockedout -or $_.Enabled -eq $false}).count

    manual:
    $i=($m|?{$_.lockedout -or $_.enabled -like 'false'}).count

    As an aside don't use aliases for cmdlets and parameters in scripts. if a puppy is killed every time you use write-host you don't want to see what happens when aliases are used in scripts.

    You don't need to use -property * on Get-ADuser you can list the extra properties you need. It'll cut down the data returned.

    In this line
    InactiveCount=@($members|?{$_.lockedout -or $_.Enabled -eq $false}).count

    why do you need the @

    $members is already an array and you are just filtering the members

    The errors are probably due to AD not understanding what you are trying to do. Piping the output of get-adgroupmember into get-aduser should work

  • #9770

    notarat
    Participant

    [quote=9758]Your logic isn’t identical in the script and manual steps

    script:
    InactiveCount=@($members|?{$_.lockedout -or $_.Enabled -eq $false}).count

    manual:
    $i=($m|?{$_.lockedout -or $_.enabled -like ‘false’}).count

    As an aside don’t use aliases for cmdlets and parameters in scripts. if a puppy is killed every time you use write-host you don’t want to see what happens when aliases are used in scripts.

    You don’t need to use -property * on Get-ADuser you can list the extra properties you need. It’ll cut down the data returned.

    In this line
    InactiveCount=@($members|?{$_.lockedout -or $_.Enabled -eq $false}).count

    why do you need the @

    $members is already an array and you are just filtering the members

    The errors are probably due to AD not understanding what you are trying to do. Piping the output of get-adgroupmember into get-aduser should work

    [/quote]

    It seems I pasted the wrong code into the post. Sorry.

    I am using the following code:

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'My OU Here'
        foreach ($group in $groups) {
    $member=Get-ADGroupmember $group | Get-ADuser -property *
    $props=@{
        groupname=$group.name
        Members=$member.count
        Inactive=($member | Where {$_.Enabled -eq 'False'}).count
        }
        New-Object PsObject -property $props
     }
    

    This code seems to work. However, I have encountered some issues I am not knowledgeable enough to handle.

    Specifically, My OUs are set up with Groups in a Resource container under my main OU and my Users are in a Users container. When the script pipes the Get-ADgroupmember to Get-ADUser and encounters someone's account that resides in another OU (or even another domain in our forest) it throws errors for that user account (but does continue to run)

    My Groups container is:

    OU=Resource Groups,OU=Groups,OU=City,OU=state,DC=EastDomain,DC=Headquarters,DC=company,DC=com
    

    My Users container is:

    OU=USERS,OU=CITY,OU=STATE,DC=EastDomain,DC=Headquarters,DC=Company,DC=com
    

    I guess my questions would be:

    Is it possible to look for the groups in the Resource Groups container (for the initial get-adgroup command) and then, when get-adgroupmember is executed and starts listing the user names, look up those member accounts in the Users container?

    In essence, the code would look something like: (if this were possible)

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'OU=Resource Groups,OU=Groups,OU=City,OU=state,DC=EastDomain,DC=Headquarters,DC=company,DC=com'
        foreach ($group in $groups) {
    $member=Get-ADGroupmember $group | Get-ADuser -f * OU=USERS,OU=CITY,OU=STATE,DC=EastDomain,DC=Headquarters,DC=Company,DC=com -property *
    $props=@{
        groupname=$group.name
        Members=$member.count
        Inactive=($member | Where {$_.Enabled -eq 'False'}).count
        }
        New-Object PsObject -property $props
     }
    
  • #9771

    Dave Wyatt
    Moderator

    It shouldn't matter what OU the users are in, but if the groups contain members from other domains, that's where you might start to see AD Referral exceptions.

    This is mostly guesswork, for the moment, since I don't have a multi-domain test environment set up. However, I'm guessing that when you make a single call to Get-ADUser, it connects to a single domain controller and tries to process all of the objects in the pipeline against that DC (and the cmdlet isn't enabling ADSI's "chase referrals" option under the hood). So if you pipe distinguished names from multiple domains, some of them will wind up getting referrals instead of actual objects. You can probably solve this by using the DirectorySearcher and DirectoryEntry classes instead of the AD cmdlets, which gives you control over the referral chasing behavior. You might also be able to get around the problem by making a separate call to Get-ADUser for each group member.

    Here's a modification of the code that tries the second approach, making a separate call to Get-ADUser for each group member. See how it works:

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'My OU Here'
    
    foreach ($group in $groups) {
        $inactiveUsers = 0
    
        $members = Get-ADGroupMember $group
        foreach ($member in $members)
        {
            if ($member.objectClass -eq 'user')
            {
                try
                {
                    $user = Get-ADUser -Identity $member.DistinguishedName -ErrorAction Stop
    
                    if ($user.Enabled -eq $false)
                    {
                        $inactiveUsers++
                    }
                }
                catch
                {
                    Write-Error -ErrorRecord $_
                }
            }
        }
    
        $props=@{
            groupname = $group.name
            Members = $members.count
            Inactive = $inactiveUsers
        }
    
        New-Object PsObject -property $props
    }
    
  • #9785

    notarat
    Participant

    [quote=9771]It shouldn’t matter what OU the users are in, but if the groups contain members from other domains, that’s where you might start to see AD Referral exceptions.

    This is mostly guesswork, for the moment, since I don’t have a multi-domain test environment set up. However, I’m guessing that when you make a single call to Get-ADUser, it connects to a single domain controller and tries to process all of the objects in the pipeline against that DC (and the cmdlet isn’t enabling ADSI’s “chase referrals” option under the hood). So if you pipe distinguished names from multiple domains, some of them will wind up getting referrals instead of actual objects. You can probably solve this by using the DirectorySearcher and DirectoryEntry classes instead of the AD cmdlets, which gives you control over the referral chasing behavior. You might also be able to get around the problem by making a separate call to Get-ADUser for each group member.

    Here’s a modification of the code that tries the second approach, making a separate call to Get-ADUser for each group member. See how it works:

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'My OU Here'
    
    foreach ($group in $groups) {
        $inactiveUsers = 0
    
        $members = Get-ADGroupMember $group
        foreach ($member in $members)
        {
            if ($member.objectClass -eq 'user')
            {
                try
                {
                    $user = Get-ADUser -Identity $member.DistinguishedName -ErrorAction Stop
    
                    if ($user.Enabled -eq $false)
                    {
                        $inactiveUsers++
                    }
                }
                catch
                {
                    Write-Error -ErrorRecord $_
                }
            }
        }
    
        $props=@{
            groupname = $group.name
            Members = $members.count
            Inactive = $inactiveUsers
        }
    
        New-Object PsObject -property $props
    }
    

    [/quote]

    Dave,

    Thanks for the response!

    Using your code I see errors thrown when the script tries to get-aduser for someone located in our western region domain. (expected)

    New-Object PsObject -property $props
    } : Cannot find an object with identity: 'CN= john.public,OU=USERS,OU=City,OU=State,DC=WestRegion,DC=headquarters,D
    C=company,DC=com under: 'DC=EastRegion,DC=headquarters,DC=company,DC=com'.
        + CategoryInfo          : ObjectNotFound: (CN=john.public...,DC=company,DC=com:ADUser) [Write-Error], ADIdentityNotFoundException
        + FullyQualifiedErrorId : Cannot find an object with identity: 'CN= john.public,OU=USERS,OU=city,OU=state,DC=westregion,DC=headquarters,DC=company,DC=com' under: 'DC=eastregion,DC=headquarters,DC=company,DC=com'.
    

    This does not stop the script from continuing to run and I am totally okay with that 🙂

    Some notes on the output:

    When there are zero inactive accounts assigned to a group, the output is a '0' as would be expected.
    Example:
    0 SecGroupA 4
    When the group has no members, there is no output. in essence, the number under the "members" column for that group is " (null)
    Example:
    0 SecurityGroupName
    When a group has only other groups as members, the output is {} (literally a pair of curly brackets)
    Example:
    0 SecurityGroup1 {}

  • #9786

    Dave Wyatt
    Moderator

    OK, looks like Get-ADUser is not behaving like I had hoped (searching other domains if the DN you pass it isn't in the local domain). I'm sure that can be tweaked with some combination of the -Server, -Partition and/or -SearchBase arguments, but at this point I would probably just fall back to using ADSI instead of the AD cmdlets, if it were my script (because I'm more familiar with how it behaves).

    Here's an easy workaround for the quirky behavior of $members.Count:

    Import-Module ActiveDirectory
    $groups = Get-ADGroup -f * -searchbase 'My OU Here'
    
    foreach ($group in $groups) {
        $inactiveUsers = 0
        $memberCount = 0
        
        $members = Get-ADGroupMember $group
        foreach ($member in $members)
        {
            $memberCount++
            
            if ($member.objectClass -eq 'user')
            {
                try
                {
                    $user = Get-ADUser -Identity $member.DistinguishedName -ErrorAction Stop
    
                    if ($user.Enabled -eq $false)
                    {
                        $inactiveUsers++
                    }
                }
                catch
                {
                    Write-Error -ErrorRecord $_
                }
            }
        }
    
        $props=@{
            groupname = $group.name
            Members = $memberCount
            Inactive = $inactiveUsers
        }
    
        New-Object PsObject -property $props
    }
    
    

You must be logged in to reply to this topic.