Querying Active Directory from SQL Server 2005 - sql

How can I query Active Directory from SQL Server 2005?

Pretty general question but here are some pointers.
You need a linked server creating on the SQL Server that points to ADSI (Active Directory Service Interface) something like this will do it.
EXEC sp_addlinkedserver 'ADSI', 'Active Directory Services 2.5', 'ADSDSOObject', 'adsdatasource'
Then you can use the following sort of query.
SELECT *
FROM OPENQUERY(ADSI, 'SELECT sAMAccountName
FROM ''LDAP://DC=MyDC,DC=com,DC=uk''
WHERE objectCategory = ''Person''
AND objectClass = ''user''')
You'll need to set the LDAP:// line appropriately (ask your AD admin for the details) and be aware that distributed adhoc queries using OpenQuery are disabled by default in SQL Server. Once you have the above though it should be pretty easy to google for any particular variations.

Yes.
Linked server:
EXEC master.dbo.sp_addlinkedserver
#server = N'ADSI',
#srvproduct=N'Active Directory Services',
#provider=N'ADsDSOObject',
#datasrc=N'Servername.domain.com'
Query:
select * from openquery
(
ADSI,'SELECT name
FROM ''LDAP://Servername.domain.com''
WHERE objectCategory = ''Person'' AND objectClass = ''user''
')
There are lots of examples if you search linked server and LDPA on Google.
I say this because LDAP can be quite complicated to work with.

In order to overcome the maximum limit of 1000 records returned at a time from the Active Directory queries, you can use the function which I wrote below.
CREATE FUNCTION [dbo].[tf_GetAllUsersFromActiveDirectory]
()
RETURNS
#USERS TABLE
(
sAMAccountName VARCHAR(25) PRIMARY KEY CLUSTERED
, givenName VARCHAR(200)
, SN VARCHAR(200)
, userAccountControl VARBINARY(8)
, mail VARCHAR(200)
)
AS
BEGIN
INSERT INTO #Users
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=A*)(sAMAccountName=B*)(sAMAccountName=C*)(sAMAccountName=D*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=E*)(sAMAccountName=F*)(sAMAccountName=G*)(sAMAccountName=H*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=I*)(sAMAccountName=J*)(sAMAccountName=K*)(sAMAccountName=L*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=M*)(sAMAccountName=N*)(sAMAccountName=O*)(sAMAccountName=P*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=Q*)(sAMAccountName=R*)(sAMAccountName=S*)(sAMAccountName=T*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=U*)(sAMAccountName=V*)(sAMAccountName=W*)(sAMAccountName=X*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
UNION ALL
SELECT sAMAccountName,givenName, sn, userAccountControl,mail FROM OpenQuery(ADSI, '<LDAP://YourDomain.com:389>;(&(objectClass=User)(|(sAMAccountName=Y*)(sAMAccountName=Z*)) );sAMAccountName,givenName, sn, mail,userAccountControl;subtree')
RETURN
END
GO

Just a note; to remove the link use
exec sp_dropserver 'ADSI';

Related

Getting database names, usernames via query for multi database instance/database server(s) - results showing in separate tables,

I've got a small issue I'm not seeing the solution for. I'm running a combined query for the database instance name, database name and users in that database.
Here is my query:
SELECT ##SERVERNAME AS 'Server Name'
use db1
SELECT DB_NAME() AS [Current Database];
select name as username,
create_date,
modify_date,
type_desc
from sys.database_principals
where type not in ('A', 'G', 'R', 'X')
and sid is not null
and name != 'guest'
order by username;
SELECT ##SERVERNAME AS 'Server Name'
use db2
SELECT DB_NAME() AS [Current Database];
select name as username,
create_date,
modify_date,
type_desc
from sys.database_principals
where type not in ('A', 'G', 'R', 'X')
and sid is not null
and name != 'guest'
order by username;
SELECT ##SERVERNAME AS 'Server Name'
use etc
SELECT DB_NAME() AS [Current Database];
select name as username,
create_date,
modify_date,
type_desc
from sys.database_principals
where type not in ('A', 'G', 'R', 'X')
and sid is not null
and name != 'guest'
order by username;
The issue with the output is that its not usable even though its correct. (I'm trying to manually copy paste this into a text file).
My goal would be to get the output of this query into a CSV or other filetype.
Could you please help me with this.
As yu want to do this against each database, I'm going to use some (shameless) self promotion, and use a Procedure I created a called sp_foreachdatabase: A CURSOR free version of sp_msforeachdb. There is a built in procedure that does something similar, however, it is undocumented, and sometimes misbehaves. It also uses Cursors, which many of those experienced with SQL Server know to avoid as much as possible.
Once the above is created, I then change your query a little to put the server and database into columns instead, and then use the Pre and Post Command parameters to CREATE and SELECT from a temporary table I INSERT to data into.
This gives the following statement, after you have created the objects:
USE master;
GO
DECLARE #Command nvarchar(MAX) = N'INSERT INTO #Users (servername, databasename, username, create_date, modify_date, type_desc)
SELECT ##SERVERNAME AS servername,
& as databasename,
[name] as username,
create_date,
modify_date,
type_desc
FROM sys.database_principals
WHERE type NOT IN (''A'', ''G'', ''R'', ''X'')
AND sid IS NOT NULL
AND name != ''guest''
ORDER BY username;',
#Pre_Command nvarchar(MAX) = N'CREATE TABLE #Users (servername sysname, databasename sysname, username sysname, create_date datetime, modify_date datetime, type_desc nvarchar(60));',
#Post_Command nvarchar(MAX) = N'SELECT * FROM #Users;',
#Command_Run nvarchar(MAX);
EXEC sp_foreachdatabase #Command = #Command,
#Skip_System = 1,
#Auto_Use = 1,
#Pre_Command = #Pre_Command,
#Post_Command = #Post_Command,
#Command_Run = #Command_Run OUTPUT;
--PRINT #Command_Run;
--SELECT #Command_Run;

Syntax to query LDAP over SSL

I was able to query LDAP over port 636 with the below. How do I modify it so I can query the below AD path:
"OU=Staff,OU=Accounts,OU=ABC PROD,DC=Abc,DC=com"
=============================================================
SELECT top 900 * FROM OpenQuery (
ADSI,
'SELECT *
FROM ''LDAP://ABC.com:636''
WHERE objectClass = ''User''
')
Thank you so much..
Doing trial an error, this seems to work. Thank you.
SELECT top 900 * FROM OpenQuery (
ADSI,
'SELECT *
FROM ''LDAP://abc.com:636/OU=Staff,OU=Accounts,OU=ABC PROD,DC=abc,DC=com''
WHERE objectClass = ''User''
'
)

Create a View in SQL with a Concatenated Column > Then use Case Statement to affect values

I created a view with a concatenated column named DisplayName, using the code below.
create view DisplayNames as
select FirstName + ' ' + LastName DisplayName,
*
from Table1
Is there a way to change specific DisplayName values using a case statement (or a better method)
Something like:
case
when DisplayName = 'Robert Jones' THEN 'Bob Jones'
when DisplayName = 'Thomas Simms' THEN 'Tommy Simms'
or
when FirstName = 'Robert' AND LastName = 'Jones' THEN 'Bob Jones'
In SQL Server, I would suggest APPLY:
create view DisplayNames as
select (case when v.DisplayName = 'Bob Jones' then'Thomas Simms'
else v.DisplayName
end) as DsiplayName,
t1.*
from Table1 t1 cross apply
(values (FirstName + ' ' + LastName)) v(DisplayName);
This allows you to define the column in the FROM clause. Then you don't need to figure out how to remove the old value -- as would be necessary with a CTE or subquery.
Is this what you want?
create view DisplayNames as
select
case FirstName
when 'Robert' then 'Bob'
when 'Thomas' then 'Tommy'
end
+ ' '
+ LastName DisplayName,
t.*
from Table1 t
This applies the transcodification to the first name separately, regardless of the last name.

Tsql queries into XML using a format

I've never used XML before. I have the following queries - that need to be output into XML file in a particular format
---- Show Server details
GO
SELECT
##servername as ServerName,
##version as Environment,
SERVERPROPERTY('productversion'),
SERVERPROPERTY ('InstanceName')
-- Show DB details
SELECT
name AS DBName ,
Collation_name as Collation,
User_access_Desc as UserAccess,
Compatibility_level AS CompatiblityLevel ,
state_desc as Status,
Recovery_model_desc as RecoveryModel
FROM sys.databases
ORDER BY Name
-- Sysadmin Roles
SELECT
p.name AS [Name],
r.type_desc,
r.is_disabled,
r.default_database_name
FROM
sys.server_principals r
INNER JOIN
sys.server_role_members m ON r.principal_id = m.role_principal_id
INNER JOIN
sys.server_principals p ON p.principal_id = m.member_principal_id
WHERE
r.type = 'R' and r.name = N'sysadmin'
-- Find all users associated with a database
DECLARE #DB_USers TABLE
(DBName sysname, UserName varchar(max), LoginType sysname, AssociatedRole varchar(max))--,create_date datetime,modify_date datetime)
INSERT #DB_USers
EXEC sp_MSforeachdb
use [?]
SELECT ''?'' AS DB_Name,
case prin.name when ''dbo'' then prin.name + '' (''+ (select SUSER_SNAME(owner_sid) from master.sys.databases where name =''?'') + '')'' else prin.name end AS UserName,
prin.type_desc AS LoginType,
isnull(USER_NAME(mem.role_principal_id),'''') AS AssociatedRole
FROM sys.database_principals prin
LEFT OUTER JOIN sys.database_role_members mem ON prin.principal_id=mem.member_principal_id
WHERE prin.sid IS NOT NULL and prin.sid NOT IN (0x00) and
prin.is_fixed_role <> 1 AND prin.name NOT LIKE ''##%'''
SELECT
DBName,UserName ,LoginType ,
STUFF(
(
SELECT ',' + CONVERT(VARCHAR(500), AssociatedRole)
FROM #DB_USers user2
WHERE
user1.DBName=user2.DBName AND user1.UserName=user2.UserName
FOR XML PATH('')
)
,1,1,'') AS Permissions_user
FROM #DB_USers user1
GROUP BY
DBName,UserName ,LoginType --,create_date ,modify_date
ORDER BY DBName,UserName
--List of all the jobs currently running on server
SELECT
job.job_id,
notify_level_email,
name,
enabled,
description,
step_name,
command,
server,
database_name
FROM
msdb.dbo.sysjobs job
INNER JOIN
msdb.dbo.sysjobsteps steps
ON
job.job_id = steps.job_id
-- Show details of extended stored procedures
SELECT * FROM master.sys.extended_procedures
I don't know where to start

Stored Procedure and SELECT statement return different results

I have a stored procedure on a SQL Server 2008 database. The stored procedure is very simple, just a SELECT statement. When I run it, it returns 422 rows. However, when I run the SELECT statement from the stored procedure, it returns 467 rows. I've tried this by running both the stored procedure and the SELECT statement in the same SSMS window at the same time, and the behavior is the same. The stored procedure is:
USE [REMS]
GO
/****** Object: StoredProcedure [mobile].[GetAllMobileDeviceUsers] Script Date: 12/04/2014 */
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [mobile].[GetAllMobileDeviceUsers]
AS
SET NOCOUNT ON
SELECT
ee.EmployeeID,
EmployeeName = LastName + ', ' + FirstName
FROM EmployeeInvData ee
--UNION
--SELECT
--m.EmployeeID,
--EmployeeName = LastName + ', ' + FirstName
--FROM mobile.MiscPersonnel m
INNER JOIN Employee e
ON ee.EmployeeID = e.EmployeeID
UNION
SELECT
'-1',
'- Select An Employee -'
ORDER BY EmployeeName
When I do this in the same SSMS window:
exec mobile.GetAllMobileDeviceUsers
SELECT
ee.EmployeeID,
EmployeeName = LastName + ', ' + FirstName
FROM EmployeeInvData ee
--UNION
--SELECT
--m.EmployeeID,
--EmployeeName = LastName + ', ' + FirstName
--FROM mobile.MiscPersonnel m
INNER JOIN Employee e
ON ee.EmployeeID = e.EmployeeID
UNION
SELECT
'-1',
'- Select An Employee -'
ORDER BY EmployeeName
I get two result sets. The first is 422 rows; the second is 467 rows. Why?
This could have been caused by not qualifying your object names. If objects are not fully qualified, SQL will assume they fall under the default schema.
Like this
SELECT col1 FROM dbname.schemaname.tablename
Not this
SELECT col1 FROM tablename
Check out this blog post by Aaron Bertrand for an in-depth explanation and example of this topic.