How to work around the paging limitation in SQL Link Server when targeting the Active Directory.
When we create a SQL Linked Server to AD, there are certain limitations. For example, we cannot retrieve multivalued attributes from AD. More information on limitations with SQL Linked Server queries can be found in support.microsoft.com/kb/299410
As mentioned in the KB article, queries that would return a result set larger than the default page are truncated to the default page size. This blog post will focus on a workaround for retrieving more than the default page size of records from the active directory by using a VBS to create a temporary table on the SQL server.
The following steps describe the workaround:
1. Write a VBScript which will fetch "n" numbers of records from AD, based on any Search Query and Search Filter. This VBScript will create a table in SQL by name "AD_Records_Table" to store records from the AD. The script will check for the existence of the table. If the table does not exist, the script will create it. If the table does exist, the script first truncates the table and then rebuilds the table from the data just returned from the AD.
2. From SQL Server, we will invoke this VBScript by using xp_cmdShell. Before calling xp_cmdShell we need to enable "Show Advanced Option" by calling sp_configure.
Now we have the requested data stored in the table "AD_Records_Table", we can run any SQL query required on this table.
The script can be schedule to run once a day to sync up AD & SQL Data.
In this sample script we are these fields from AD sAMAccountName, givenName, sn, distinguishedName.
Here are the detailed steps:
Step 1:
Create the following VBScript. Open a notepad, paste the following script. Save the file by name Search_User.vbs.
Please note the following script will retrieve all the user objects and display it's AdsPath in the output. You can customize the output as per the requirement.
'<C:\Scripts\Search_Users.vbs>
Option Explicit
Dim con, com
Dim ConAD, ComAD, RecAD
Dim strLDAPPath, objRootDSE, strCommand
Dim strSAM, strFirstName, strLastName, strDN
Set con = CreateObject("ADODB.Connection")
Set com = CreateObject("ADODB.Command")
con.Provider = "SQLOLEDB.1"
con.ConnectionString = "Provider='sqloledb'; Data Source='SHALOINT'; Initial Catalog='Master'; User ID= 'sa'; Password = 'Password!'"
con.Open
com.ActiveConnection= con
com.CommandText = "IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME='AD_Records_Table') BEGIN " & _
"Create Table AD_Records_Table (" &_
"sAMAccountName VARCHAR(20)," &_
"givenName VARCHAR(180)," &_
"sn VARCHAR(180)," &_
"distinguishedName VARCHAR(180) )" &_
"END ELSE TRUNCATE TABLE AD_Records_Table"
com.Execute
Set ConAD = CreateObject("ADODB.Connection")
Set ComAD = CreateObject("ADODB.Command")
Set RecAD = CreateObject("ADODB.Recordset")
ConAD.Provider = "ADsDSOObject"
ConAD.Open
Set objRootDSE = GetObject("LDAP://RootDSE")
strLDAPPath = "<LDAP://" + objRootDSE.Get("defaultNamingContext") + ">"
strCommand = strLDAPPath & ";(&(sAMAccountName=*)(objectClass=User)(objectCategory=Person));sAMAccountName,givenName,sn,distinguishedName;subtree"
ComAD.CommandText = strCommand
ComAD.ActiveConnection = ConAD
ComAD.Properties("Page Size") = 1000
Set RecAD = ComAD.Execute
Do until RecAD.EOF
strSAM = TRIM(RecAD("sAMAccountName"))
strFirstName = TRIM(RecAD("givenName"))
strLastName = TRIM(RecAD("sn"))
strDN = TRIM(RecAD("distinguishedName"))
com.CommandText = "INSERT INTO AD_Records_Table(sAMAccountName,givenName,sn,distinguishedName)" & _
"VALUES('" & strSAM & "','" & strFirstName & "', '" & strLastName & "', '" & strDN & "')"
com.Execute
RecAD.MoveNext
Loop
Set ComAD = Nothing
Set RecAD = Nothing
Set con = Nothing
Set con = Nothing
Set com = Nothing
'</C:\Scripts\Search_Users.vbs>
Step 2:
Open SQL Server's Query analyzer. Type the following SQL Query.
EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdShell', 1
GO
RECONFIGURE
GO
xp_cmdShell "Cscript C:\Scripts\Search_Users.vbs //nologo"
GO
By executing xp_cmdShell, it will display the returned records as shown in the following image:.