0

I have this code in PowerShell that looks into groups and users and creates a tab delimited txt file with the fields that I want.

However it only finds half of the information. I am trying to replace another process that generates an identical report and this report gathers around 580,000 lines of data where as this PowerShell report only generates around 300,000.
I think I need to look into groups recursively, but I'm not sure how to do that.

code:

#requires -version 2

$ScriptName = $MyInvocation.MyCommand.Name
$ADS_GROUP_TYPE_SECURITY_ENABLED = 0x80000000
$PageSize = 250  # Adjust as needed

# Create the Pathname object and enable its EscapedMode property
$ADS_ESCAPEDMODE_ON = 2
$ADS_SETTYPE_DN = 4
$ADS_FORMAT_X500_DN = 7
$Pathname = new-object -comobject "Pathname"
[Void] $Pathname.GetType().InvokeMember("EscapedMode", "SetProperty", $NULL, $Pathname, $ADS_ESCAPEDMODE_ON)

# Returns correctly escaped DN using Pathname object
function Get-EscapedPath {
  param(
    [String] $distinguishedName
  )
  [Void] $Pathname.GetType().InvokeMember("Set", "InvokeMethod", $NULL, $Pathname, ($distinguishedName, $ADS_SETTYPE_DN))
  $Pathname.GetType().InvokeMember("Retrieve", "InvokeMethod", $NULL, $Pathname, $ADS_FORMAT_X500_DN)
}

# Returns a property from a ResultPropertyCollection if it's defined
function Get-SearchResultProperty {
  param(
    [System.DirectoryServices.ResultPropertyCollection] $properties,
    [String] $propertyName
  )
  if ( $properties[$propertyName] ) {
    $properties[$propertyName][0]
  }
  else {
    ""
  }
}

# Returns a property from a DirectoryEntry if it's defined
function Get-DirEntryProperty {
  param(
    [System.DirectoryServices.DirectoryEntry] $dirEntry,
    [String] $propertyName
  )
  if ( $dirEntry.$propertyName ) {
    $dirEntry.$propertyName[0]
  }
  else {
    ""
  }
}

write-progress $ScriptName "Enumerating groups"
$domain = [ADSI] ""
$searcher = [ADSISearcher] "(objectClass=group)"
$searcher.SearchRoot = $domain
$searcher.PageSize = $PageSize
$searcher.SearchScope = "subtree"; 
$searcher.PropertiesToLoad.AddRange(@("name","grouptype","distinguishedname","description","managedby","member"))
$searchResults = $searcher.FindAll()
$groupCounter = 0
$groupCount = $searchResults.Count
foreach ( $searchResult in $searchResults ) {
  $properties = $searchResult.Properties
  $domainName = "domainname"
  $groupName = Get-SearchResultProperty $properties "name"
  $groupType = Get-SearchResultProperty $properties "grouptype"
  if ( ($groupType -band $ADS_GROUP_TYPE_SECURITY_ENABLED) -ne 0 ) {
    $groupTypeString = "Security"
  }
  else {
    $groupTypeString = "Distribution"
  }
  $groupDescription = Get-SearchResultProperty $properties "description"
  $groupDN = Get-SearchResultProperty $properties "distinguishedname"
  $groupManagedBy = Get-SearchResultProperty $properties "managedby"
  $member = $properties["member"]
  if ( $member ) {
    $memberCounter = 0
    $memberCount = ($member | measure-object).Count
    foreach ( $memberDN in $member ) {
      $memberDirEntry = [ADSI] "LDAP://$(Get-EscapedPath $memberDN)"
      "" | select-object `
        @{Name = "Domain";             Expression = {$domainName}},
        @{Name = "Group Name";         Expression = {$groupName}},
        @{Name = "Type";               Expression = {$groupTypeString}},
        @{Name = "Description";        Expression = {$groupDescription}},
        @{Name = "Distinguished Name"; Expression = {$groupDN}},
        @{Name = "Managed By";         Expression = {$groupManagedBy}},
        @{Name = "Members";            Expression = {$memberDN}},
        @{Name = "Full Name";          Expression = {Get-DirEntryProperty $memberDirEntry "name"}},
        @{Name = "User Name";          Expression = {Get-DirEntryProperty $memberDirEntry "samaccountname"}},
        @{Name = "Display Name";       Expression = {Get-DirEntryProperty $memberDirEntry "displayname"}}
      $memberCounter++
      $memberPercent = ($memberCounter / $memberCount) * 100 -as [Int]
      $params = @{
        "Activity" = $ScriptName
        "Completed" = $memberPercent -eq 100
        "CurrentOperation" = "Enumerating '$groupDN'"
        "PercentComplete" = $memberPercent
        "Status" = "Groups: {0}/{1} [{2:P2}] - Members: {3}/{4} [{5:P2}]" -f
          $groupCounter,
          $groupCount,
          ($groupCounter / $groupCount),
          $memberCounter,
          $memberCount,
          ($memberCounter / $memberCount)
      }
      write-progress @params
    }
  }
  else {
    # Group contains no members
    "" | select-object `
      @{Name = "Domain";             Expression = {$domainName}},
      @{Name = "Group Name";         Expression = {$groupName}},
      @{Name = "Type";               Expression = {$groupTypeString}},
      @{Name = "Description";        Expression = {$groupDescription}},
      @{Name = "Distinguished Name"; Expression = {$groupDN}},
      @{Name = "Managed By";         Expression = {$groupManagedBy}},
      @{Name = "Members";            Expression = {""}},
      @{Name = "Full Name";          Expression = {""}},
      @{Name = "User Name";          Expression = {""}},
      @{Name = "Display Name";       Expression = {""}}
  }
  $groupCounter++
  $groupPercent = ($groupCounter / $groupCount) * 100 -as [Int]
  $params = @{
    "Activity" = $ScriptName
    "Completed" = $groupPercent -eq 100
    "CurrentOperation" = "Enumerating '$groupDN'"
    "PercentComplete" = $groupPercent
    "Status" = "Groups: {0}/{1} [{2:P2}]" -f
      $groupCounter,
      $groupCount,
      ($groupCounter / $groupCount)
  }
  write-progress @params
  # Periodically force garbage collection to reduce memory usage
  if ( ($groupCounter % $PageSize) -eq 0 ) {
    [GC]::Collect()
    [GC]::WaitForPendingFinalizers()
  }
} 
$searchResults.Dispose()

EDIT: I have tried using this line, just before the line containing "subtree":

$searcher.Filter = "(member:1.2.840.113556.1.4.1941:=*)"

and tried editing this line:

$searcher = [ADSISearcher] "(objectClass=group)"

to this

$searcher = [ADSISearcher] "(&(objectClass=group)(memberof:1.2.840.113556.1.4.1941L:=*))"

Neither work, it just returns immediately with no output, presumably as the filter is not picking up anything. I didn't have a filter before because I wanted everything

FredoAF
  • 504
  • 2
  • 8
  • 22
  • I answered a similar question in : [Listing users in ad group recursively with powershell script without CmdLets](http://stackoverflow.com/a/8055996/608772) – JPBlanc Sep 26 '14 at 11:43

1 Answers1

0

There's an LDAP filter (1.2.840.113556.1.4.1941) for that:

LDAP Matching Rule in Chain

mjolinor
  • 66,130
  • 7
  • 114
  • 135