-
Notifications
You must be signed in to change notification settings - Fork 162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix big group problem #204
Conversation
My concerns with this approach is that now instead of potentially 1 query to get the AD group members we are using 1 query to get the members and then n queries to get the ADUser object for the sAMAccountName/SID for each of those users. While it may work this would be a very chatty operation and I would hope we can do better than that. The other issue is that this just returns user principals, we also need to deal with other groups, computer accounts, etc. At first I tried to replicate the timeout problem by creating a group with 10000 users and while I cannot seem to replicate the timeout problem I do get a different error saying the size limit has been reached. New-ADGroup -Name MyGroup -GroupScope Global
1..10000 | ForEach-Object -Process {
New-ADUser -Name "Test$_" -PassThru | ForEach-Object { Add-ADGroupMember -Identity "MyGroup" -Members $_ }
}
Using a slightly tweaked version of what's in the PR this is what happens Measure-Command { &{
Get-ADGroup -Identity MyGroup -Properties Member |
Select-Object -ExpandProperty Member |
ForEach-Object {
Get-ADUser $_ -Properties sAMAccountName, sid
}
} }
Days : 0
Hours : 0
Minutes : 0
Seconds : 29
Milliseconds : 118
Ticks : 291186293
TotalDays : 0.000337021172453704
TotalHours : 0.00808850813888889
TotalMinutes : 0.485310488333333
TotalSeconds : 29.1186293
TotalMilliseconds : 29118.6293 30 seconds isn't too bad but this is a fairly basic AD test environment I have. I assume that in an actual environment this will be higher based on various environment settings. We could just have an LDAP filter that gets all the objects Following that article you linked to I think we can do one better to get only the sAMAccountName and SID that is required for each member rather than the whole ADUser object and also 1 one query. $group = Get-ADGroup MyGroup
# Can probably find tune this to filter for specific types but this just a test
$filter = "(memberOf=$($group.DistinguishedName))"
Measure-Command { & { Get-ADObject -LDAPFilter $filter -Properties sAMAccountName, objectSID } }
Days : 0
Hours : 0
Minutes : 0
Seconds : 1
Milliseconds : 659
Ticks : 16592064
TotalDays : 1.92037777777778E-05
TotalHours : 0.000460890666666667
TotalMinutes : 0.02765344
TotalSeconds : 1.6592064
TotalMilliseconds : 1659.2064 This cuts down the time a bit more but I'm not sure whether that's just a byproduct on my small AD environment. This is quick because it's just 1 call/network trip but the server still needs to scan each object and IIRC this won't work if a member is in a different domain (could be wrong about that though). The next route would be to do a similar operation as Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$Context = [DirectoryServices.AccountManagement.PrincipalContext]::new('Domain')
$group = Get-ADGroup MyGroup
Measure-Command { & {
$members = [DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity(
$Context, 'DistinguishedName', $group.DistinguishedName).Members
foreach ($member in $members) {
[PSCustomObject]@{sAMAAccountName = $member.sAMAAccountName; Sid = $member.Sid}
}
} }
Days : 0
Hours : 0
Minutes : 0
Seconds : 8
Milliseconds : 38
Ticks : 80387648
TotalDays : 9.30412592592592E-05
TotalHours : 0.00223299022222222
TotalMinutes : 0.133979413333333
TotalSeconds : 8.0387648
TotalMilliseconds : 8038.7648 While not as fast as the LDAPFilter approach it is still faster than query each object separately and maybe the time taken could be faster if the AD environment is quite large. The only thing I can see being faster is to get a list of member and then building a large It would be interesting if you were able to try each of the approaches here and share the timings for your environment. We might be able to select one of these for this PR and fix the original problem you are trying to solve. |
Thank you for your super detailed analysis. |
Hi @jborean93 After running my tests I confirm the ldap filter approach is the fastest. For your references here are the results:
Days : 0
Hours : 0
Minutes : 0
Seconds : 3
Milliseconds : 697
Ticks : 36972850
TotalDays : 4.2792650462963E-05
TotalHours : 0.00102702361111111
TotalMinutes : 0.0616214166666667
TotalSeconds : 3.697285
TotalMilliseconds : 3697.285
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 333
Ticks : 3338137
TotalDays : 3.86358449074074E-06
TotalHours : 9.27260277777778E-05
TotalMinutes : 0.00556356166666667
TotalSeconds : 0.3338137
TotalMilliseconds : 333.8137
Days : 0
Hours : 0
Minutes : 0
Seconds : 0
Milliseconds : 487
Ticks : 4876865
TotalDays : 5.64451967592593E-06
TotalHours : 0.000135468472222222
TotalMinutes : 0.00812810833333333
TotalSeconds : 0.4876865
TotalMilliseconds : 487.6865 I propose to optimize the ldap query like that: (&(objectClass=groupOfNames)(memberOf=$($group.DistinguishedName))) I've also spotted a missing @extra_args in the last Remove-ADPrincipalGroupMembership: I'm going to update the PR with these two changes. Thank you, |
@@ -50,7 +50,11 @@ if ($diff_mode) { | |||
$result.diff = @{} | |||
} | |||
|
|||
$members_before = Get-AdGroupMember -Identity $ADGroup @extra_args | |||
$group = Get-ADGroup $ADGroup @extra_args |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to run this again, you can just use $ADGroup
which was retrieved above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right. I've fixed it
@@ -50,7 +50,11 @@ if ($diff_mode) { | |||
$result.diff = @{} | |||
} | |||
|
|||
$members_before = Get-AdGroupMember -Identity $ADGroup @extra_args | |||
$group = Get-ADGroup $ADGroup @extra_args | |||
$filter = " (&(objectClass=groupOfNames)(memberOf=$($group.DistinguishedName)))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain the objectClass=groupOfNames
part? When using this filter I get nothing returned back to me on some of my AD groups.
There's also a leading space here that probably isn't needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...good catch: it doesn't make sense. I propose to remove the class filter as we can have groups of any ldap object class.
Sorry for the delay, I just one question around the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your feedback. I proposed a solution and pushed a new version.
I'm happy to discuss in case you have any additional question.
@@ -50,7 +50,11 @@ if ($diff_mode) { | |||
$result.diff = @{} | |||
} | |||
|
|||
$members_before = Get-AdGroupMember -Identity $ADGroup @extra_args | |||
$group = Get-ADGroup $ADGroup @extra_args |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right. I've fixed it
@@ -50,7 +50,11 @@ if ($diff_mode) { | |||
$result.diff = @{} | |||
} | |||
|
|||
$members_before = Get-AdGroupMember -Identity $ADGroup @extra_args | |||
$group = Get-ADGroup $ADGroup @extra_args | |||
$filter = " (&(objectClass=groupOfNames)(memberOf=$($group.DistinguishedName)))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...good catch: it doesn't make sense. I propose to remove the class filter as we can have groups of any ldap object class.
Thanks for working on this! The CI failure is unrelated so I'll merge this in. |
SUMMARY
win_domain_group_membership might fail if the group has a lot of members
ISSUE TYPE
COMPONENT NAME
win_domain_group_membership
ADDITIONAL INFORMATION
Get-ADGroupMember might hit a timeout if used against a big group. The behavior is described here: https://pscustomobject.github.io/powershell/powershell%20tips/Get-AdGroupMember-Timeout/
I'm proposing a fix to this problem by implementing the solution proposed in the article.
The change isn't a breaking change as the output object's format doesn't change.
Please feel free to accept this PR in case you think the enhancement might be useful.