Tsql 通过SQL递归查询AD组成员

Tsql 通过SQL递归查询AD组成员,tsql,active-directory,sql-server-2008-r2,adsi,ldap-query,Tsql,Active Directory,Sql Server 2008 R2,Adsi,Ldap Query,背景 我正在创建一些SQL来帮助进行安全审计;这将从各种系统数据库和Active Directory中获取安全信息,并生成所有异常情况的列表(即,在一个系统中关闭帐户但在其他系统中未关闭帐户的情况) 当前代码 要获取作为安全组成员的用户列表,我运行以下SQL: if not exists(select 1 from sys.servers where name = 'ADSI') EXEC sp_addlinkedserver 'ADSI', 'Active Directory Ser

背景

我正在创建一些SQL来帮助进行安全审计;这将从各种系统数据库和Active Directory中获取安全信息,并生成所有异常情况的列表(即,在一个系统中关闭帐户但在其他系统中未关闭帐户的情况)

当前代码

要获取作为安全组成员的用户列表,我运行以下SQL:

if not exists(select 1 from sys.servers where name = 'ADSI') 
    EXEC sp_addlinkedserver 'ADSI', 'Active Directory Services 2.5', 'ADSDSOObject', 'adsdatasource'

SELECT sAMAccountName, displayName, givenName, sn, isDeleted --, lastLogonTimestamp --, lastLogon (Could not convert the data value due to reasons other than sign mismatch or overflow.)
FROM OPENQUERY(ADSI
, 'SELECT sAMAccountName, displayName, givenName, sn, isDeleted
FROM ''LDAP://DC=myDomain,DC=myCompany,DC=com''
WHERE objectCategory = ''Person''
AND objectClass = ''user'' 
AND memberOf = ''CN=mySecurityGroup,OU=Security Groups,OU=UK,DC=myDomain,DC=myCompany,DC=com''
')
order by sAMAccountName
问题/问题

我希望这段代码能够递归地工作;也就是说,如果用户是某个组的成员,而该组又是指定组的成员,那么它们也应该被包括在内(对于完整的层次结构)。有人知道如何通过SQL做到这一点吗

更新

我现在已经解决了一些问题(与引用的问题无关,但与我遇到的其他一些问题有关)

  • lastLogon出现错误。这是因为服务器版本为x86。使用x64数据库解决了此问题
  • lastLogon以数字形式返回。添加了一些代码以将其转换为DateTime2
  • 通过使OpenQuery本身成为动态的,我能够将组名从硬编码字符串中移出,因此在OpenQuery的上下文中,生成的字符串看起来是静态的

像这样

--Get all members of a group
SELECT cn,AdsPath 
FROM OPENQUERY (ADSI, '<LDAP: dc="corp,dc=mycorp,dc=com">;(&(objectCategory=person)(memberOf:1.2.840.113556.1.4.1941:=CN=Administrators,CN=Builtin,DC=corp,DC=mycorp,DC=com));cn, adspath;subtree')
ORDER BY cn; 

--get all groups a user is a member of
SELECT cn,AdsPath
FROM OPENQUERY (ADSI, '<LDAP: dc="corp,dc=mycorp,dc=com">;(&(objectClass=group)(member:1.2.840.113556.1.4.1941:=CN=John Doe,OU=Developers,OU=Staff,DC=corp,DC=mycorp,DC=com));cn, adspath;subtree')
ORDER BY cn;
——获取组的所有成员
选择cn、AdsPath
来自OPENQUERY(ADSI,;(&(objectCategory=person)(memberOf:1.2.840.113556.1.4.1941:=CN=Administrators,CN=Builtin,DC=corp,DC=mycorp,DC=com));CN,adspath;子树')
cn发出的命令;
--获取用户所属的所有组
选择cn、AdsPath
来自OPENQUERY(ADSI,;(&(objectClass=group)(成员:1.2.840.113556.1.4.1941:=CN=johndoe,OU=Developers,OU=Staff,DC=corp,DC=mycorp,DC=com));CN,adspath;子树')
cn发出的命令;

查看递归搜索条件。

虽然这是一篇老文章,但谷歌仍然喜欢将其放在搜索结果的顶部,因此,当我为同样的问题苦苦挣扎时,我想发布我的发现/解决方案,这要归功于Riverway让我走上了正确的轨道

创建存储过程:

CREATE PROCEDURE [dbo].[GetLdapUserGroups]
    (
    @LdapUsername NVARCHAR(max)
    )
AS
BEGIN
DECLARE @Query NVARCHAR(max), @Path NVARCHAR(max)

SET @Query = '
    SELECT @Path = distinguishedName
    FROM OPENQUERY(ADSI, ''
        SELECT distinguishedName 
        FROM ''''LDAP://DC=DOMAIN,DC=COM''''
        WHERE 
            objectClass = ''''user'''' AND
            sAMAccountName = ''''' + @LdapUsername + '''''
    '')
'

EXEC SP_EXECUTESQL @Query, N'@Path NVARCHAR(max) OUTPUT', @Path = @Path OUTPUT 

  SET @Query = '
    SELECT cn AS [LdapGroup]
    FROM OPENQUERY (ADSI, ''<LDAP://DOMAIN.COM>;
    (&(objectClass=group)(member:1.2.840.113556.1.4.1941:= ' + @Path + '));
    cn, adspath;subtree'')
    ORDER BY cn;
'

EXEC SP_EXECUTESQL @Query
END
然后,我使用哈希表将AD组与SQL数据以及最终用户应该看到的内容正确匹配

DECLARE @RptPermissions table (ldapGroup nvarchar(max),scholarshipCode nvarchar(50),gender nvarchar(2))
INSERT INTO @RptPermissions VALUES('EMP_Enrollment_Admissions','ALL','MF')
在我的例子中,我使用它来提取SSRS用户变量,并将其传递到查询中,以便根据广告组成员资格选择记录

;WITH CTE_Permissions AS
(
    SELECT
        p.scholarshipCode
        ,p.gender
    FROM @UserGroup AS g
    JOIN @RptPermissions AS p ON
        g.ldapGroup = p.ldapGroup
)
…稍后在查询中

JOIN CTE_Permissions AS p ON
         s.SCHOLARSHIP_ID = p.scholarshipCode
         OR p.scholarshipCode = 'ALL'

希望这能有所帮助。

ps.我还有一些关于这方面的子问题-我还没有在这里发布,因为我还在谷歌工作,但我想我会在评论中提到,以防有人能提供帮助。有人知道如何获取
lastLogon
值吗?有人知道如何使SQL动态化吗(也就是说,我可以为安全组使用一个变量,而不是在查询中对其进行硬编码)?提前感谢大家。
lastLogon
问题在于DB(服务器;而不是客户端)是x86。该功能仅在x64上起作用。解决了一些小问题-有关新代码,请参阅上面的更新。尽管如此,上述问题仍然存在。
;WITH CTE_Permissions AS
(
    SELECT
        p.scholarshipCode
        ,p.gender
    FROM @UserGroup AS g
    JOIN @RptPermissions AS p ON
        g.ldapGroup = p.ldapGroup
)
JOIN CTE_Permissions AS p ON
         s.SCHOLARSHIP_ID = p.scholarshipCode
         OR p.scholarshipCode = 'ALL'