首页 > 解决方案 > T/SQL LDAP 查询以获取真实的 lastLoginTimestamp 值

问题描述

我有成功检索用户数据的 SQL 代码,包括 lastLoginTimestamp 值。现在,问题是我们有多个 DC 服务器,并且 lastLoginTimestamp 值通常仅在另一个服务器的值是 14 天时才跨服务器传播。这意味着,为了获得“真实”的 lastLoginTimestamp 值,您需要向每个 DC Server 查询它们自己的本地值,并在真实的 lastLoginTimestamp 值处取最大值。

我的问题是......你如何独立查询每台服务器?

下面是我的有效代码,但只查询一台服务器(显然是为我自动选择的?)。所以我的问题是......如何在 SQL 中查询每个域控制器(动态),以便我可以获得 lastLoginTimestamp 的最新值?

-----------------------------------------
-- Initialize Variables
-----------------------------------------
DECLARE @strSQL         NVARCHAR(MAX)

-----------------------------------------
-- Retrieve All AD Users In My OU
-----------------------------------------
SET @strSQL =
'SELECT sAMAccountName,
        Mail,
        DistinguishedName,
        whenCreated,
        lastLogonTimestamp,
        pwdLastSet
    FROM ''''LDAP://MyDomain.com/OU=Users,OU=MyGroup,DC=MyDomain,DC=com''''
    WHERE objectClass = ''''User''''
'

-----------------------------------------
-- Wrap SQL in OpenQuery Command
-----------------------------------------
SET @strSQL = 'SELECT DISTINCT
                  sAMAccountName AS UserName,
                  Mail AS EMail,
                  whenCreated AS CreatedOnDate,
                  CASE
                    WHEN (lastLogonTimestamp IS NULL) THEN NULL
                    WHEN (CAST(lastLogonTimestamp AS BIGINT) = 0) THEN NULL
                    ELSE CAST((CAST(lastLogonTimestamp AS BIGINT) / 864000000000.0 - 109207) S DATETIME)
                  END AS LastLogon,
                  CASE
                    WHEN (pwdLastSet IS NULL) THEN NULL
                    WHEN (CAST(pwdLastSet AS BIGINT) = 0) THEN NULL
                    ELSE CAST((CAST(pwdLastSet AS BIGINT) / 864000000000.0 - 109207) AS DATETIME)
                  END AS LastPWReset
            FROM OPENQUERY([MyLinkedServer], ''' + @strSQL + ''')
          '

-----------------------------------------
-- Execute Statement
-----------------------------------------
EXEC SP_EXECUTESQL @strSQL

标签: sqlsql-serveractive-directoryldaplinked-server

解决方案


首先要做的事——这在 SQL Server 之外更好(也更容易)实现。但是,情况往往就是这样......

您面临的问题是,Active Directory 域/林中的域控制器 (DC) 列表可以是动态查询,以便在添加和删除 DC 时进行查询,并且正如您所指出的,通用查询将绑定到域供应商决定的控制器是最好的。在非常高的级别上,DC 通常位于带有 DNS 查询的位置,这很难(充其量)在 SQL Server 中执行。但是,您可以针对 AD 全局目录执行目录查询以获取 DC 列表。

这应该会为您提供域/林中所有 DC 的 CN 和 DN 列表。

SELECT cn
FROM OPENQUERY(adsi, 'SELECT cn, distinguishedName
    FROM ''GC://MyDomain.com/DC=MyDomain,DC=COM''
    WHERE objectClass = ''computer''
    and userAccountControl = 532480'
)

(注意:532480 是域控制器的 UAC 值 - 请参阅此处以供参考)

您可以稍微修改现有代码以迭代该结果列表,以便您可以直接绑定到从上面的查询中发现的每个域控制器:

DECLARE @CN VARCHAR(1000);
DECLARE @DN VARCHAR(1000);
DECLARE DC_LIST CURSOR FAST_FORWARD FOR
SELECT cn, distinguishedName
FROM OPENQUERY(adsi, 'SELECT cn, distinguishedName
    FROM ''GC://MyDomain.com/DC=MyDomain,DC=COM''
    WHERE objectClass = ''computer''
    and userAccountControl = 532480'
);

OPEN DC_LIST;
FETCH NEXT FROM DC_LIST INTO @CN, @DN;

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @DomainPath VARCHAR(1000) = SUBSTRING(@DN, CHARINDEX('DC=', @DN), LEN(@DN));
    DECLARE @Domain VARCHAR(1000) = REPLACE(@DomPath, 'DC=', '.')

    SET @strSQL =
    'SELECT sAMAccountName,
            Mail,
            DistinguishedName,
            whenCreated,
            lastLogonTimestamp,
            pwdLastSet
        FROM ''''LDAP://' + @CN + @Domain + '/' + @DomainPath + '''''
        WHERE objectClass = ''''User''''
    '

    [...do the rest of your querying with OPENQUERY...]

    FETCH NEXT FROM DC_LIST INTO @CN, @DN;
END;

CLOSE DC_LIST;
DEALLOCATE DC_LIST;

您可以在遍历游标时将 OPENQUERY 的结果插入到临时表中,然后最后从临时表中选择您的最新登录日期时间。


推荐阅读