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