powershell query of sys.sql_logins - sql-server

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')

Related

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#.

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'

Pass a powershell variable into a SQL value during out-datatable (invoke-sqlcmd2)

I want to insert a PowerShell variable value with a Select as I build a datatable from a SQL query.
Borrowed function invoke-sqlcmd2 from TechNet gallery and dot-sourced it in.
$NewSequenceID = invoke-sqlcmd2 -ServerInstance "MyServer" -Database "MyDB" -Query "INSERT INTO [Sequence] (TimeStarted) SELECT GETDATE(); SELECT max(SequenceID) as SequenceID FROM [Sequence]" | foreach { $_.SequenceID }
This generates a new sequence ID and stamps the time we started the batch. Results in a single number which will identify this run. Verified with 'write $NewSequenceID'.
I want to keep later results from queries together with this SequenceID for analysis.
Then I have this:
$PollTime = Get-Date -format "yyyy-MM-dd HH:mm:ss"
Then I want to do this: (Edit: This statement is not working - error message at the bottom)
$AuditUserOutput = invoke-sqlcmd2 -ServerInstance "MyServer2" -Database "MyDB2" -Query "SELECT $NewSequenceID, $PollTime, [USERID], [PID], [UDATE] FROM [MyTable]" -As 'Datatable'
And do some things with the table, then write it after with write-datatable.
If I select NULL for the first two values and grab the other three from the existing table, it works fine. I want to add the $NewSequenceID and $PollTime from the previous statements.
I've read a dozen pages about using ` (backtick), $, {}, and on and on, but I haven't gotten it right. Can someone help with the correct syntax for inserting these variable values into the selection?
PS Error is: Exception calling "Fill" with "1" argument(s): "Invalid pseudocolumn "$NewSequenceID"."
You're interpolating the variables correctly in PowerShell. If I'm understanding this correctly, the problem is with your SQL query. I'm going to make an inference here, but I think this is probably what you want:
$AuditUserOutput = invoke-sqlcmd2 -ServerInstance "MyServer2" -Database "MyDB2" -Query "SELECT [NewSequenceID], [PollTime], [USERID], [PID], [UDATE] FROM [MyTable] WHERE NewSequenceID = '$NewSequenceID' AND PollTime = '$PollTime'" -As 'Datatable'
If not, please clarify by responding to the questions above.
I was able to work around this by first creating a variable to store the query text, which allowed for the natural substitution I needed:
$AuditUserQuery = "SELECT '$NewSequenceID', '$PollTime', [USERID], [PID], [UDATE] FROM [AUDITUSER]"
Then calling that variable as the $query when building the datatable.
This avoided the parameterization problem experienced before.

How get multi value data form Active Directory using SQL

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.

T-SQL query to show table definition?

What is a query that will show me the full definition, including indexes and keys for a SQL Server table? I want a pure query - and know that SQL Studio can give this to me, but I am often on "wild" computers that have only the most bare-bones apps and I have no rights to install studio. But SQLCMD is always an option.
UPDATE: I have tried sp_help, but is just yields one record which shows Name, Owner, Type and Created_Datetime. Is there something else I am missing with sp_help?
Here is what I call:
sp_help airports
Note that I really do want the DDL that defines the table.
There is no easy way to return the DDL. However you can get most of the details from Information Schema Views and System Views.
SELECT ORDINAL_POSITION, COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Customers'
SELECT CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE
WHERE TABLE_NAME = 'Customers'
SELECT name, type_desc, is_unique, is_primary_key
FROM sys.indexes
WHERE [object_id] = OBJECT_ID('dbo.Customers')
Have you tried sp_help?
sp_help 'TableName'
Visit http://www.stormrage.com/SQLStuff/sp_GetDDL_Latest.txt.
You will find the code of sp_getddl procedure for SQL Server.
The purpose of the procedure is script any table, temp table or object.
USAGE:
exec sp_GetDDL GMACT
or
exec sp_GetDDL 'bob.example'
or
exec sp_GetDDL '[schemaname].[tablename]'
or
exec sp_GetDDL #temp
I tested it on SQL Server 2012, and it does an excellent job.
I'm not the author of the procedure. Any improvement you make to it send to Lowell Izaguirre (scripts#stormrage.com).
The easiest and quickest way I can think of would be to use sp_help
sp_help 'TableName'
Use this little Windows command-line app that gets the CREATE TABLE script (with constraints) for any table. I've written it in C#. Just compile it and carry it on a memory stick. Perhaps someone can port it to Powershell.
using System;
using System.Linq;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
namespace ViewSource
{
public class ViewSource
{
public static void Main(string[] args)
{
if (args.Length != 6)
{
Console.Error.WriteLine("Syntax: ViewSource.exe <server>" +
" <user> <password> <database> <schema> <table>");
}
Script(args[0], args[1], args[2], args[3], args[4], args[5]);
}
private static void Script(string server, string user,
string password, string database, string schema, string table)
{
new Server(new ServerConnection(server, user, password))
.Databases[database]
.Tables[table, schema]
.Script(new ScriptingOptions { SchemaQualify = true,
DriAll = true })
.Cast<string>()
.Select(s => s + "\n" + "GO")
.ToList()
.ForEach(Console.WriteLine);
}
}
}
Since SQL 2012 you can run the following statement:
Exec sp_describe_first_result_set #tsql= N'Select * from <yourtable>'
If you enter a complex select statement (joins, subselects, etc), it will give you the definition of the result set. This is very handy, if you need to create a new table (or temp table) and you don't want to check every single field definition manually.
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-describe-first-result-set-transact-sql
sp_help 'YourTableName'
This will return columns, datatypes, and indexes defined on the table:
--List all tables in DB
select * from sysobjects where xtype = 'U'
--Table Definition
sp_help TableName
This will return triggers defined on the table:
--Triggers in SQL Table
select * from sys.triggers where parent_id = object_id(N'SQLTableName')
I know it's an old question, but exactly what I was looking for. Because I want to batch script some tables, I rewrote the C# code from Anthony Faull for PowerShell.
This one is uses Integrated Security:
Import-Module sqlps
$serverInstance = "<server>"
$database = "<database>"
$table = "<table>"
$schema = "<schema>"
$options = New-Object -TypeName Microsoft.SqlServer.Management.Smo.ScriptingOptions
$options.DriAll = $true
$options.SchemaQualify = $true
$connection = New-Object -TypeName Microsoft.SqlServer.Management.Common.ServerConnection `
-ArgumentList $serverInstance
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server `
-ArgumentList $connection
$server.Databases.Item($database).Tables.Item($table, $schema).Script($options) `
| ForEach-Object -Process { $_ + "`nGO"}
And here with username and password:
Import-Module sqlps
$serverInstance = "<server>"
$user = "<user>"
$password = "<pasword>"
$database = "<database>"
$table = "<table>"
$schema = "<schema>"
$options = New-Object -TypeName Microsoft.SqlServer.Management.Smo.ScriptingOptions
$options.DriAll = $true
$options.SchemaQualify = $true
$connection = New-Object -TypeName Microsoft.SqlServer.Management.Common.ServerConnection `
-ArgumentList $serverInstance
$connection.LoginSecure = $false
$connection.Login = $user
$connection.Password = $password
$server = New-Object -TypeName Microsoft.SqlServer.Management.Smo.Server `
-ArgumentList $connection
$server.Databases.Item($database).Tables.Item($table, $schema).Script($options) `
| ForEach-Object -Process { $_ + "`nGO"}
Simply type the table name and select it and press ATL + F1
Say your table name is Customer then open a new query window, type and select the table name and press ALT + F1
It will show the complete definition of table.
A variation of #Anthony Faull's answer for those using LINQPad:
new Server(new ServerConnection(this.Connection.DataSource))
.Databases[this.Connection.Database]
.Tables["<table>", "dbo"]
?.Script(new ScriptingOptions {
SchemaQualify = true,
DriAll = true,
})
You'll need to reference 2 assemblies:
Microsoft.SqlServer.ConnectionInfo.dll
Microsoft.SqlServer.Smo.dll
And add namespace references as mentioned in Anthony's snippet.
SELECT ORDINAL_POSITION, COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
, IS_NULLABLE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'EMPLOYEES'
Try the sp_help stored procedure.
sp_help <>
Another way is to execute sp_columns procedure.
EXEC sys.sp_columns #TABLE_NAME = 'YourTableName'
As an addition to Barry's answer. The sp_help can also be used by itself to iterate all of the objects in a particular database. You also have sp_helptext for your arsenal, which scripts out programmatic elements, like stored procedures.
General table definition is shown using stored procedure sp_help, as said before:
sp_help 'table_name_in_current_db_context'
When using tables of multiple contexts is useful to prefix the command above with the desired context, instead of changing them with use db_xyz:
DB_Products..sp_help 'MyTable' -- for definition of MyTable in DB_Products
And it works with temp tables also:
tempdb..sp_help '#TempTable' -- for definition of #TempTable in current context.
There is an easy way to get DDL scripts for any database object.
Open SQL Server Management Studio
Connect to the source database server.
Expand the databases tree.
Right-click on the database with the tables to export.
In the sub-menu, expand "Tasks".
In the sub-menu, choose "Generate Scripts..."
Use the wizard to choose the objects to export.
For the scripting options, choose 1 script per object.
This will export a file for every table, view, stored proc, user, role, or schema you chose. Take that to the destination computer.

Resources