How get multi value data form Active Directory using SQL - sql-server

Is it possible to get multi value properties form AD like description, memberOf. if I run simply by adding memberOf this gives error
select *
FROM OPENQUERY(ADSI,'SELECT initials, samAccountName, displayName, distinguishedName, mail, memberOf FROM ''LDAP://DC=corp, DC=contoso, DC=com'' WHERE objectClass=''Person''')
Error:
Msg 7346, Level 16, State 2, Line 1
Cannot get the data of the row
from the OLE DB provider "ADSDSOObject" for linked server "ADSI".
Could not convert the data value due to reasons other than sign
mismatch or overflow.
This is because of memberOf is multi valued property in Active Directory. I am using SQL Server 2008 R2

No, you cannot do this - and there's no "trick" or hack to get it to work, either.
The ADSI provider for SQL Server is rather limited - not supporting multi-valued attributes is one of those limitations.
So you'll need to find another way to do this, e.g. by using SQl-CLR integration and accessing the Active Directory through .NET, or by e.g. exposing the data you need as a web service that you consume from SQL Server.

While you can't use ADSI to return memberof you can query memberof so if you have a group you want to check against you can do the following where extenstionAttribute3 is the employee ID:
SELECT displayName
FROM OPENQUERY(ADSI,
'SELECT displayName
FROM ''LDAP://DC=company,DC=com''
WHERE memberof = ''CN=staff,OU=SharepointGroups,DC=company,DC=com''
AND extensionAttribute3 = ''12345678''
')
If the return value is not null then you can assume the user is part of the group.

Here is my trick / hack for getting this to work:
exec xp_cmdshell 'powershell -command "get-aduser -filter * -properties SamAccountName, <MultiValue> | Select samaccountname, <MultiValue>"'
Change <MultiValue> to whatever attribute you are trying to pull. It will output the values as comma delimited in SQL. Change the PowerShell cmdlet as needed. All you have to do is collect the output, format it, and join it on your other data. Also, be sure your server has the AD PowerShell module and that you have enabled xp_cmdshell in SQL.

just wrote a sql script to include description (multi-value) field on our company Intranet directory. What I did was export-csv delimited using powershell and then bulk insert that info into a table. The simplest solution for me as we only have about 650 employees (records).
exec xp_cmdshell 'powershell.exe -command "get-aduser -filter * -properties SamAccountName, Description,GivenName,sn,title,telephoneNumber,mobile,mail,physicalDeliveryOfficeName| Select SamAccountName, Description,GivenName,sn,title,telephoneNumber,mobile,mail,physicalDeliveryOfficeName| export-csv -encoding unicode -delimiter "`t" -path C:\SQLJobs\addir.csv -notype"'
Go
CREATE TABLE dbLiftowDir.dbo.ADDir
(
[SamAccountName] NVARCHAR(4000),
[Description] NVARCHAR(4000),
[GivenName] NVARCHAR(4000),
[sn] NVARCHAR(4000),
[title] NVARCHAR(4000)COLLATE French_CI_AS NOT NULL,
[telephoneNumber] NVARCHAR(4000),
[mobile] NVARCHAR(4000),
[mail] NVARCHAR(4000),
[physicalDeliveryOfficeName] NVARCHAR(4000),
)
BULK
INSERT dbLiftowDir.dbo.ADDir
FROM 'C:\SQLJobs\addir.csv'
WITH
(
CODEPAGE = 'ACP',
DATAFILETYPE ='char',
FIELDTERMINATOR = '\t',
ROWTERMINATOR = '\n',
FIRSTROW = 2
Other things I did was remove " from field values using set column replace value, and deleting rows that were non-human accounts. As I found it to be easier to do in SQL instead of passing the code into powershell.

Related

powershell query of sys.sql_logins

I'm trying automate some queries and am querying sys.sql_logins and get different results between a query in ssms and powershell.
My query is this:
$SQLquery =#"
Select 'sql_logins' [sql_logins], ##SERVERNAME [Hostname], DB_NAME() [Database], * from sys.sql_logins;
"#
When I run that from a query in ssms i the SID result as 0xA1E3(some long alphanumeric string). When i run the same thing via powershell - i get a complete different format for the SID. Instead of the long alphanumeric string I end up with something like this {123, 45, 70, 16...} and i have no idea why or how to make it match the result i get via a manual query in ssms.
why does it change? how do i get it to NOT change and remain like 0xA1E3(super long alphanumeric string)?
[edit - as requested]
Really not doing anything special to get or display the data. Invoke-sqlcmd and then spit it out once returned.
$result = invoke-sqlcmd -query $SQLquery -serverinstance $computername -database $dbname -Username $dbuser -Password $dbpass
$result | Format-Table -Property Hostname, Database, name, principal_id, sid,`
type, type_desc, is_disabled, create_date, modify_date,`
default_database_name, default_language_name, credential_id,`
is_policy_checked, is_expiration_checked, password_hash -Autosize
The problem is correctly identified in another answer to this question in that the varbinary value being returned from the query is being treated as a byte array by powershell. My recommendation as to a fix is different, though - change the formatting in the query. If I change your query to:
Select 'sql_logins' [sql_logins],
##SERVERNAME [Hostname],
DB_NAME() [Database],
convert(varchar(172), sid, 1) as sid
from sys.sql_logins;
And then run it through the rest of your code, it works for me. NB: I didn't do select * in my query - if you really need all of the columns, you should list them explicitly. Lastly, one observation - sys.sql_logins is a server-level DMV; querying it on a per-database basis is going to be duplicative for all databases on the same server.
Edit - changed the length of the varchar to accommodate the varbinary(85) SID as the fine manual says. Showing my work, (85 bytes * 2 characters/byte) + (2 characters for '0x') = 172.
The SID which you get on the result window of SSMS is a 16 byte (binary(16)) literal value based on a GUID, while the one which you get in PowerShell is as mentioned in the comments, a default view is a comma separated list of byte values surrounded with brackets. PowerShell is better at string manipulation than SQL Server hence it internally converts the binary(16) value to a byte value.
Here is what you can do -
$SQLquery = "Select 'sql_logins' [sql_logins], ##SERVERNAME [Hostname], DB_NAME() [Database], * from sys.sql_logins;"
$Result = Invoke-Sqlcmd -query $SQLquery -ServerInstance ServerName -Database master
$Result[0].sid
$Result[0].sid.Length would give you 1024, implying that the length is indeed a KB. Use the ConvertTo-SQLHashString function from Mike Fal's blog where he talks about copying SQL Logins via PowerShell -
function ConvertTo-SQLHashString{
param([parameter(Mandatory=$true)] $binhash)
$outstring = '0x'
$binhash | ForEach-Object {$outstring += ('{0:X}' -f $_).PadLeft(2, '0')}
return $outstring
}
ConvertTo-SQLHashString $Result[0].sid
That would give you a long hex value. You could use the PowerShell TrimEnd() function to get the results like those in SSMS window -
(ConvertTo-SQLHashString $Result[0].sid).TrimEnd('0')

A positional parameter cannot be found that accepts argument "from" when doing db2 query to get all schemas

I am getting an error when trying to run a db2 select query to get all the schemas listed in a db2 database.
Here is the error:
Select-Object : A positional parameter cannot be found that accepts
argument 'from' .At line:5 char:1...
I don't see the issue here since this appears to be the correct format. My code is below:
$conn = New-Object System.Data.OleDb.OleDbConnection("Provider=IBMDADB2;Database=TESTDATABASE;HostName=DB2HOST.db2domain.net;Protocol=TCPIP;Port=50002;Uid=adminID;Pwd=aPassWord;")
$ds = New-Object System.Data.DataSet
$conn.Open();
set-item -path env:DB2CLP -value "**$$**"
Select schemaname from syscat.schemata;
Your Select is a bare-line command in PowerShell. Select is aliased to Select-Object and that's why you get the error. You need to issue your Select to the database.
See examples like:
How To Execute MS Access Query with OLEDB.12
https://technet.microsoft.com/en-us/library/hh855069.aspx
Note that this one incorrectly marks the PowerShell examples as C#.

TSQL: access user data from LDAP

I have an Active Directory (LDAP) that stores user information. I'm trying to access it using TSQL, but I'm having authentication problems.
The LDAP looks like this (I edited the data):
The user has the following properties:
Now, I'm trying to get the info from this user through a TSQL query from SQL Server using OPENROWSET like so:
SELECT *
FROM
OPENROWSET('ADSDSOObject','adsdatasource'; 'domain.com.io\test';'the_sha1_pass',
'SELECT givenname
FROM ''LDAP://domain.com.io/ou=Users,
dc=domain,dc=com,dc=io'' WHERE givenname = ''Test''
')
But when I execute it I get the following error:
Server: Msg 7399, Level 16, State 1, Line 1 OLE DB provider '
ADSDSOObject ' reported an error. The provider indicates that the user
did not have the permission to perform the operation.
Now, I don't know if I have to send the authentication of the user I'm trying to query over, or the credentials I use to connect with LDAPAdmin. And if I have to send it, should I send the pass already encrypted or without encryption?
Thanks.
You can change your OPENROWSET query as follows by removing the username and password
SELECT * FROM OPENROWSET('ADSDSOObject','adsdatasource',
'SELECT givenname
FROM ''LDAP://kodyaz.com/DC=kodyaz,DC=com'' WHERE givenname = ''KODYAZ''
')
Or as an alternative, you can try following query structure
SELECT * FROM OpenQuery(ADSI, 'SELECT * FROM ''LDAP://kodyaz.com/DC=kodyaz,DC=com'' WHERE objectCategory=''user'' AND givenname = ''KODYAZ'' ')
I added user objectCategory in WHERE clause with givenname parameter in OpenQuery command
Before running above Select statement, you can add LDAP as linked server in SQL Server as shown in referred tutorial
EXEC sp_addlinkedserver 'ADSI', 'Active Directory Service Interfaces', 'ADSDSOObject', 'adsdatasource'

Powershell Invoke-Sqlcmd - return multiple datasets

I'm looking for suggestions on either returning multiple datasets, or keeping the session open, with Invoke-SqlCmd?
I have a series of SQL queries that use temporary tables to generate a few related views of the data that I am sending on (via Excel) to a manager. As we work on what is required from the datasets, I am getting a little tired of cutting and pasting samples into Excel.
I thought to use Powershell to simply send the results to HTML files as output for the manager, however I ran into a couple of problems
If I put the final extracts into one SQL file, Powershell appends all of the data into a single result set (sort of a union of the tables)
If I attempt to build the temporary tables and then extract each query individually, each Invoke-Sqlcmd is a seperate session, meaning my Temporary tables get dropped.
I'm looking for suggestions on either returning multiple datasets, or keeping the session open?
Invoke-Sqlcmd -InputFile .\GenerateTimecard.sql -Variable $params | Out-Null;
#{
'Summary' = 'select * from #WeeklyTimeSummary;'
'ByDay' = 'select * from #WeeklyTimeDaily order by postdate desc;'
'ByTask' = 'select * from #WeeklyTimeEvents order by HoursSpent desc;'
'Detail' = 'select * from #WeeklyTimeDetail order by postdate desc;'
}.GetEnumerator() | ForEach-Object {
Write-Output $_.Name;
$fname = $_.Name + '.html';
Invoke-Sqlcmd -Query $_.Value | ConvertTo-Html | Out-File -Encoding ascii $fname;
};
The Description section from Get-Help Invoke-Sqlcmd says it supports GO commands so you could try running everything at once. Personally I'd use the -InputFile parameter and pipe the results to Out-File.
You can specify the ApplicationName parameter for Invoke-SqlCmd, which results in a different SQL connection.
Omitting ApplicationName will result in the temp tables getting removed the second time you call Invoke-SqlCmd.
Something like:
Invoke-SqlCmd -ApplicationName CreateTable -Query 'CREATE TABLE ##FooTable (FooKey INT)
Invoke-SqlCmd -ApplicationName SelectTable -Query 'SELECT * FROM ##FooTable'

powershell logging results to DB

How can I save the results from a Powershell command to a MS SQL DB?
For example:
I run this at a Powershell prompt: get-psdrive and it returns some results in a column view.
How can I take each element of the result and log it into a separate DB row/column?
I recommend saving the results of your command to a variable. Such as:
$drives = Get-PSDrive
The variable can be indexed like this:
First Element:
$drives[0]
Last Element:
$drives[-1]
You can iterate through each element with foreach:
foreach ($drive in $drives) {
# current drive is $drive
}
Or the ForEach-Object cmdlet:
$drives | ForEach-Object {
# current drive is $_
}
Now that you have the data to populate your table with you are ready to connect to the database and perform the database record inserts.
You can make use of the Powershell SQL server cmdlets or you can connect using .NET objects. Depending on what version of SQL server you have will drive your choice on which to use. SQL Server 2008 has Powershell cmdlets, 2005 does not. There is a wealth of information about the SQL server 2008 Powershell integration here. For SQL Server 2005 you have some different options. This question answer here provides a list of Powershell options to use with SQL Server 2005.
More Info:
When Powershell displays object information it uses a type system to selectively determine what properties of the object to display on the screen. Not all of the object's are displayed. Powershell uses XML files to determine what properties to display which are stored in the Powershell directory:
dir $PSHOME/*format* | select Name
The objects returned from Get-PsDrive are of type System.Management.Automation.PSDriveInfo. The file PowerShellCore.format.ps1xml tells the formatting engine what properties to display in the Powershell window. It just might be that these are the exact properties you are looking for however many objects have additional properties that are not displayed. For example an object of type System.IO.DirectoryInfo will not have all it's properties displayed by default. You can view the rest of the objects properties using the Get-Member cmdlet, for example:
Get-Item $env:windir | Get-Member
This will show all of the object's methods and properties. You can also view all of the object's properties using the Select-Object cmdlet using a wildcard for the property parameter:
Get-Item $env:windir | Select-Object -Property *
To access an objects properties values use the following syntax:
$objectVariable.ObjectProperty
Now that you know how to view an objects properties and access their values you'll need to use this to construct an Insert SQL statement. Here is an example using the Invoke-SqlCmd cmdlet provided with SQL Server 2008.
Invoke-Sqlcmd -ServerInstance $env:COMPUTERNAME -Database Test -Query "Insert MyTable values ('a', 'b')"
Here's an example looping through objects returned from Get-PsDrive assuming you have a table called MyTable and it has at least two columns which accept textual data:
Get-PsDrive | ForEach-Object {
$providerName = $_.Name
$providerRoot = $_.Root
Invoke-Sqlcmd -ServerInstance $env:COMPUTERNAME -Database Test -Query "Insert MyTable values ('$providerName', '$providerRoot')"
}

Resources