如何通过递归LDAP查询获取AD中未过期的临时组成员关系
我完全懂你的困扰——当用AD的临时组成员功能(带MemberTimeToLive)时,过期后Get-ADGroup加-ShowMemberTimeToLive参数能正确过滤掉过期成员,但直接用LDAP传递匹配查询用户的递归组时,居然还能看到已经过期的组。这其实是因为LDAP的1.2.840.113556.1.4.1941传递匹配规则默认不会检查临时成员的过期时间,得我们手动加过滤条件才行。
先搞懂背后的原因
AD的临时成员关系是靠给组的成员项添加msDS-MemberExpirationTime属性实现的——这个属性存的是成员关系过期的时间戳(FILETIME格式)。Get-ADGroup的-ShowMemberTimeToLive参数会让PowerShell自动帮你比对这个时间戳,过滤掉过期的成员;但原生LDAP查询不会做这个默认校验,所以传递匹配会把所有历史/过期的递归成员关系都返回。
两种解决方案,按需选择
方法1:PowerShell先拿所有递归组,再逐个过滤(逻辑直观)
先通过LDAP传递查询拿到用户的所有递归组,再对每个组检查用户是否属于未过期成员。好处是逻辑简单,用PowerShell内置参数处理过期校验,缺点是组多的话速度会慢一点。
示例代码:
# 定义目标用户和DC服务器 $userDN = "CN=TEST_user,OU=Guests,OU=Special accounts,DC=TEST,DC=EXAMPLE,DC=nl" $dcServer = "EXAMPLEdc03" # 先获取用户的所有递归组 $allRecursiveGroups = Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$userDN)" -Server $dcServer # 筛选出用户是未过期成员的组 $activeGroups = foreach ($group in $allRecursiveGroups) { $groupDetails = Get-ADGroup $group -Properties Members -ShowMemberTimeToLive -Server $dcServer if ($groupDetails.Members -contains $userDN) { $group } } # 输出结果 $activeGroups | Sort-Object Name | Select Name | Format-Table
方法2:构造带过滤的LDAP查询(更高效)
如果想一步到位,直接用LDAP查询同时实现递归匹配和过期时间过滤,需要构造复合查询:既要匹配用户是组的递归成员,又要匹配该成员关系要么是永久的(无过期时间),要么过期时间在当前时间之后。
示例代码:
# 定义目标用户和DC服务器 $userDN = "CN=TEST_user,OU=Guests,OU=Special accounts,DC=TEST,DC=EXAMPLE,DC=nl" $dcServer = "EXAMPLEdc03" # 把当前时间转换成AD用的FILETIME格式 $currentFileTime = (Get-Date).ToFileTime() # 构造复合LDAP过滤器 $ldapFilter = @" (& (member:1.2.840.113556.1.4.1941:=$userDN) (| (!(msDS-MemberExpirationTime=*)) # 匹配永久成员(无过期时间属性) (msDS-MemberExpirationTime>=$currentFileTime) # 匹配未过期的临时成员 ) ) "@ -replace "`n|`r", "" # 移除换行,确保LDAP能识别 # 执行查询并输出 Get-ADGroup -LDAPFilter $ldapFilter -Server $dcServer | Sort-Object Name | Select Name | Format-Table
这个查询会直接返回所有用户是递归成员、且成员关系未过期(或永久)的组,效率比逐个查询高很多。
针对你测试案例的验证
在你的测试中,当TEST_user在TEST_x组的30秒TTL过期后,用这个LDAP查询就不会再返回TEST_x了——因为msDS-MemberExpirationTime已经小于当前时间,会被过滤掉。如果有未过期的嵌套组(比如TEST_y包含TEST_x且TEST_x的成员关系还在有效期内),查询也会正确返回TEST_y。
备注:内容来源于stack exchange,提问作者rrr

