One "Create User For Login" - For Multiple Stand-Alone Machines - sql

I've created a post-deploy script for a database project in VS, in which I am granting a Windows account "UserX" user access to a database. This post-deployment script is being used by multiple developers who need to have this "UserX" granted access on thier own machines qcnb01, qcnb03,qcnb04, etc.. (with stand-alone sql instances). Is there a way to substitute [qcnb02\UserX] with a placeholder, i.e. [qcnb??\UserX], and then insert the machine name accordingly?
Below is my create user statement.
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N'qcnb02\UserX')
CREATE USER [UserX] FOR LOGIN [qcnb02\UserX] WITH DEFAULT_SCHEMA=[dbo]
GO
Let me clarify that "UserX" is the static portion and the post-deploy script is actually looking for and inserting the machine name (qcnb??) back into the post-deploy script (i.e. [qcnb25\UserX]) during build\deploy time. Is this possible?
Thanks in advance for your help.
JDA

Try this
DECLARE #MachineName VARCHAR(100)
SELECT #MachineName = Host_Name()
DECLARE #SQL VARCHAR(1000)
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N''+#MachineName+'\UserX')
BEGIN
SET #SQL = 'CREATE USER [UserX] FOR LOGIN ['+ #MachineName+'\USERX] WITH DEFAULT_SCHEMA=[dbo]'
EXECUTE #SQL
END

DECLARE #MachineName VARCHAR(100)
SET #MachineName = Host_Name()
DECLARE #SQL VARCHAR(1000)
--IF NOT EXISTS (SELECT name FROM sys.syslogins WHERE name = N''+#MachineName+'\machine')
BEGIN
SET #SQL = 'CREATE USER [machine] FOR LOGIN ['+#MachineName+'\machine] WITH DEFAULT_SCHEMA=[dbo]'
EXECUTE (#SQL)
END

Related

Create users with SSMS in Azure SQL PaaS

We're new to using SQL as a service. Since there's no gui for user management, we're having a hard time figuring out how to create users who are actually able to log in with SSMS. I can create users with access to the proper DBs, and use those users to access the DBs programatically, but not through SSMS. How do I create multiple users who can login and administrate SQL as a service databases?
Connect to Azure SQL Database using the Admin user. You can automate creating many users using a script like below:
declare #Name varchar(100),#sql varchar(400),#sql2 varchar(400),#sql3 varchar(400) ,#sql4 varchar(400)
declare #psw varchar(10) = 'Traf#12345';
declare #T table (Name varchar(100)) ;
insert into #T values
('SampleUser1'),
('SampleUser2')
While exists(select * from #T)
begin
select top 1 #Name = name from #T
IF NOT EXISTS (SELECT [name] FROM [sys].[sql_logins] WHERE [name] = #Name)
BEGIN
SET #sql = 'CREATE LOGIN '+'['+#name+']'+' WITH PASSWORD ='''+ #psw + '''; '
EXEC (#sql)
END
DELETE FROM #T WHERE Name = #Name
END
Below is the sequence that you need to create users:
---- ON SERVER LEVEL
CREATE LOGIN TestUser
WITH PASSWORD = 'ThisIsAStrongPassword!'
GO
---- create user on Master database Level
CREATE USER TestUser
FOR LOGIN TestUser
WITH DEFAULT_SCHEMA = dbo
GO
---- create user on database level
CREATE USER TestUser
FOR LOGIN TestUser
WITH DEFAULT_SCHEMA = dbo
GO
-- if you want the user to be able to create databases and logins
exec sp_addRoleMember 'dbmanager', 'TestUser';
exec sp_addRoleMember 'loginmanager', 'TestUser'
-- in each individual database, to grant dbo
exec sp_addRoleMember 'db_owner', 'TestUser';
This document will be helpful for your requirement ,
https://azure.microsoft.com/en-in/blog/adding-users-to-your-sql-azure-database/

Executing SQL query on multiple databases

I know my post has a very similar title to other ones in this forum, but I really couldn't find the answer I need.
Here is my problem, I have a SQL Server running on my Windows Server. Inside my SQL Server, I have around 30 databases. All of them have the same tables, and the same stored procedures.
Now, here is the problem, I have this huge script that I need to run in all of these databases. I wish I could do it just once against all my databases.
I tried a couple things like go to "view" >> registered servers >> local server groups >> new server registration. But this solution is for many servers, not many databases.
I know I could do it by typing the database name, but the query is really huge, so it would take too long to run in all databases.
Does anybody have any idea if that is possible?
You can use WHILE loop over all database names and inside loop execute query with EXECUTE. I think that statement SET #dbname = ... could be better, but this works too.
DECLARE #rn INT = 1, #dbname varchar(MAX) = '';
WHILE #dbname IS NOT NULL
BEGIN
SET #dbname = (SELECT name FROM (SELECT name, ROW_NUMBER() OVER (ORDER BY name) rn
FROM sys.databases WHERE name NOT IN('master','tempdb')) t WHERE rn = #rn);
IF #dbname <> '' AND #dbname IS NOT NULL
EXECUTE ('use '+QUOTENAME(#dbname)+';
/* Your script code here */
UPDATE some_table SET ... ;
');
SET #rn = #rn + 1;
END;
Consider running the script in SQLCMD Mode from SSMS (Query--SQLCMD Mode). This way, you can save the script to a file and run it in the context of each of the desired databases easily:
USE DB1;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB2;
:r C:\SqlScript\YourLargeScript.sql
GO
USE DB3;
:r C:\SqlScript\YourLargeScript.sql
GO
This technique can also be used to run the script against databases on other servers with the addition of a :CONNECT command. The connection reverts back to initial server/database after execution of the entire script:
:CONNECT SomeServer
USE DB4;
:r C:\SqlScript\YourLargeScript.sql
GO
:CONNECT SomeOtherServer
USE DB5;
:r C:\SqlScript\YourLargeScript.sql
GO
Important gotcha: Note GO batch separators are needed for :CONNECT to work as expected. I recommend including GO in the the invoking script like the above example but GO as the last line in the :r script file will also provide the desired results. Without GO in this example (or at the end of the script file), the script would run twice on SomeServer and not run against SomeOtherServer at all.
ApexSQL Propagate is the tool which can help in this situation. It is used for executing single or multiple scripts on multiple databases, even multiple servers. What you should do is simply select that script, then select all databases against which you want to execute that script:
When you load scripts and databases you should just click the “Execute” button and wait for the results:
You can write script like this
DECLARE CURSOR_ALLDB_NAMES CURSOR FOR
SELECT name
FROM Sys.Databases
WHERE name NOT IN('master', 'tempdb')
OPEN CURSOR_ALLDB_NAMES
FETCH CURSOR_ALLDB_NAMES INTO #DB_NAME
WHILE ##Fetch_Status = 0
BEGIN
EXEC('UPDATE '+ #DB_NAME + '..SameTableNameAllDb SET Status=1')
FETCH CURSOR_ALLDB_NAMESINTO INTO #DB_NAME
END
CLOSE CURSOR_ALLDB_NAMES
this is the normal way of doing this :
suppose you want to do a select on database DBOther than it would be :
select * from DBOther..TableName
Also check if the table or view is on the dbo schema, if not you should add the schema also : Please notice I use only one dot now after the database name
select * from DBOther.dbo.ViewName
If any of the databases is on another server on another machine, than make sure the Database is in the Linked Server.
Then you can access the table or view on that database via:
SELECT * FROM [AnotherServerName].[DB].[dbo].[Table]
Here is another way that does not requires typing the database name :
use DB1
go
select * from table1
go
use DB2
go
select * from table1
go
Note that this will only work if the tables and fields are exact the same on each database
You can use the following script to run the same script on a set of databases. Just change the filter in the insert line.
declare #dbs table (
dbName varchar(100),
done bit default 0
)
insert #dbs select [name], 0 FROM master.dbo.sysdatabases WHERE [Name] like 'targets_%'
while (exists(select 1 from #dbs where done = 0))
begin
declare #db varchar(100);
select top 1 #db = dbName from #dbs where done = 0;
exec ('
use [' + #db + '];
update table1 set
col1 = '''',
col2 = 1
where id = ''45b6facb-510d-422f-a48c-687449f08821''
');
print #db + ' updated!';
update #dbs set done = 1 where dbName = #db;
end
If your SQL Server version does not support table variables, just use Temp Tables but don`t forget to drop them at the end of the script.
Depending on the requirement, you can do this:
declare #dbName nvarchar(100)
declare #script nvarchar(max)
declare #dbIndex bigint = 0
declare #dbCount bigint = (
select count(*) from
sys.databases
)
declare crs_databases cursor for
(
select
[name]
from
sys.databases
)
open crs_databases
fetch next from crs_databases into #dbName
while ##FETCH_STATUS = 0
begin
set #dbIndex = #dbIndex+1
set #script = concat(#script,
' select Id from ['+#dbName+']..YourTableName ',
case
when #dbIndex = #dbCount then ''
else 'union'
end)
fetch next from crs_databases into #dbName
end
select #script
close crs_databases
deallocate crs_databases
Please note that the double dotted notation assumes that the schema is dbo. Otherwise, you need to explicitly write down the schema.
select Id from ['+#dbName+'].schema.YourTableName
When you need to execute stored procedures on each server, the #script variable will have another content.

TSQL Stored Procedure - Create User in database from another or master database

I need to create a stored procedure (1 or more) that I can call from the master db (or other db) in a scheduled job to restore permissions (user and privs) to a database that has been restored from a source db. I do not want to create the users at the source db level because I want to restrict access to that server, so the sproc will need to be executed once the restore is completed to grant access. I currently have:
use (database);
if ( select objectproperty(object_id('dbo.spMaint_RestorePermissions_database'), 'IsProcedure') ) is null
exec ('create procedure dbo.spMaint_RestorePermissions_database as select 1');
go
alter procedure dbo.spMaint_RestorePermissions_database
as
set nocount on;
---- grant user permission to access database
create user [domain\userid] for login [domain\userid]
----- grant user access rights to database
exec sp_addrolemember 'db_datareader', 'domain\userid'
go
One of the restrictions of stored procs is that they cannot contain use database; statements. To accomplish what you're looking for, you could run sp_executesql from master and pass in a tsql string that executes sp_executesql in the context of a specific database. Something like this:
--This is the tsql statement that gets executed on a specific user db.
--You would use this to build your "create proc", "alter proc", "create user", etc statements.
DECLARE #InnerSql NVARCHAR(MAX) = 'SELECT DB_NAME()'
DECLARE #DB SYSNAME = 'DatabaseName'
DECLARE #Tsql NVARCHAR(MAX) = '[' + #DB + ']..sp_executesql N''' + #InnerSql + ''''
EXEC sp_executesql #Tsql;

Granting access to one db to users/roles of another

Short version: Can I grant access to external databases to a role?
Long version:
I am working on reports using Crystal which is retrieving data from an applications SQL Server Instance (database1).
The application is running the reports and overwriting the connection in the report, I have no access to the applications code.
I have added a new DB to the server (database2) which is collecting information from a telephone exchange and I want to join some of this information to the applications data (database1).
I can join the data and the reports work when run within the designer (logged in as SA) but when the reports are run externally through the application they fail with a fairly generic error (Failed to retrieve data).
I am assuming the error is being caused by the new DB permissions as if I log into the application as SA the error goes away.
The Application has a special DB Role for users that run reports, when adding a table/view/sp to the application db (database1) I can simply grant select/execute to this role to allow the reports to access the object.
Now I have object in a different db however the role isn't easily accessible.
Is there any way I can reference the second db (database2) through the existing role?
eg:
USE [database1]
GRANT EXECUTE ON [database2].[dbo].[CUSTOM_PROCEDURE] TO [applicationrole1]
OR
USE [database2]
GRANT EXECUTE ON [dbo].[CUSTOM_PROCEDURE] TO [database1].[dbo].[applicationrole1]
Ideally I want to be able to link to the Role somehow rather than re-creating a new role as the role is updated by the application regularly when new users are added/configured.
(Not tagged with Crystal-Reports as this isn't related to the problem)
Edit:
Is there any way to do something like:
INSERT INTO Database2.sys.database_principals
SELECT * FROM Database1.sys.database_principals
WHERE [type] = 'S'
To copy over the Users (not logins) and then add the role members?
Presumably, you'd be using a login that has access to both databases (such as the case with SA). You'd create the appropriate role and grant rights to each database, then create the user (linked to the login you're using) in both, adding each to the role you created.
The T-SQL will look something like this:
use master
go
create login testuser with password = 'mypassword123'
go
use test
go
create role reporting
grant select on something to reporting -- grant your permissions here
create user testuser for login testuser
exec sp_addrolemember 'reporting', 'testuser'
go
use test2
go
create role reporting
grant select on something2 to reporting -- grant your permissions here
create user testuser for login testuser
exec sp_addrolemember 'reporting', 'testuser'
go
Now I can connect to test and execute
select * from something
select * from test2.dbo.something2
Of course, you'd change your grants to EXECUTE on the desired stored procedures, but it looks like you've already got that covered.
After that, it's just about executing a simple script to create logins, users, and add them to the role.
declare #sql nvarchar(max), #username nvarchar(50), #password nvarchar(50)
-- ########## SET PARAMETERS HERE
SET #username = N'testguy'
SET #password = N'test123'
-- ########## END SET PARAMETERS
set #sql = N'USE master; CREATE LOGIN [' + #username + N'] WITH PASSWORD = N''' + #password + N'''; USE database1; CREATE USER [' + #username + N'] FOR LOGIN [' + #username + N']; EXEC sp_addrolemember ''reporting'', ''' + #username + N'''; USE database2; CREATE USER [' + #username + N'] FOR LOGIN [' + #username + N']; EXEC sp_addrolemember ''reporting'', ''' + #username + N''';'
exec sp_executesql #sql
Syncing logins, users, and roles automatically
This script will find all SQL logins (you can change this to whatever makes sense to you; windows AND SQL accounts, accounts that contain a certain string, whatever), ensure the user has been created in database1 and database2, and ensures they are both added to the reporting role. You will need to ensure the reporting role is created on both databases, but you only need to do this once.
After that, you can run this script periodically, either manually, or using a SQL Agent job. All you need to do is create the login for the server; when the script runs it will do the rest.
declare #login nvarchar(50), #user1 nvarchar(50), #user2 nvarchar(50), #sql nvarchar(max), #rolename nvarchar(50)
SET #rolename = 'reporting'
declare c cursor for
select sp.name as login, dp1.name as user1, dp2.name as user2 from sys.server_principals as sp
left outer join database1.sys.database_principals as dp1 on sp.sid = dp1.sid
left outer join database2.sys.database_principals as dp2 on sp.sid = dp2.sid
where sp.type = 'S'
and sp.is_disabled = 0
open c
fetch next from c into #login, #user1, #user2
while ##FETCH_STATUS = 0 begin
-- create user in db1
if (#user1 is null) begin
SET #sql = N'USE database1; CREATE USER [' + #login + N'] FOR LOGIN [' + #login + N'];'
EXEC sp_executesql #sql
end
-- ensure user is member of role in db1
SET #sql = N'USE database1; EXEC sp_addrolemember '''+ #rolename + ''', ''' + #login + N''';'
EXEC sp_executesql #sql
-- create user in db2
if (#user2 is null) begin
SET #sql = N'USE database2; CREATE USER [' + #login + N'] FOR LOGIN [' + #login + N'];'
EXEC sp_executesql #sql
end
-- ensure user is member of role in db2
SET #sql = N'USE database2; EXEC sp_addrolemember '''+ #rolename + ''', ''' + #login + N''';'
EXEC sp_executesql #sql
fetch next from c into #login, #user1, #user2
end
close c
deallocate c
You will want to add a transaction and error handling to roll off incomplete changes, but I'll leave that up to you.
Set the stored procedure to execute as owner.
http://msdn.microsoft.com/en-us/library/ms188354.aspx
Set trustworthy on the database where the stored procedure is located.
http://technet.microsoft.com/en-us/library/ms187861.aspx
Ensure that you have the same owner on both databases.
http://msdn.microsoft.com/en-us/library/ms188676(v=sql.105).aspx
Grant execute permissions on the stored procedure to the appropriate user or role on the database with the procedure.

How to rename users, add a role etc, using script in t-sql

I have to
Rename users (from yyyyyy\xxxx to xxxx)
Add a role to the users
See the priviliges of stored procedures granted to a specified role (I found a table with the information regarding tables, but not stored procedure)
All in t-sql. I know how to do it mannualy, but with 400+ users, I hope to script me out of the problems.
Can anyone help?
What you need to do is loop over the users to modify and execute the commands to make the changes you need. You can do this by querying the syslogins table and creating a cursor with the results.
I have added the statement to rename the user, but adding the role is as simple as adding in a second statement and exec with sp_addrolemember
DECLARE #Login as varchar(50);
DECLARE LoginsCrsr CURSOR FOR
SELECT name
FROM syslogins
WHERE name like '%John%'; --Whatever critera you need
OPEN LoginsCrsr;
FETCH NEXT FROM LoginsCrsr
INTO #Login;
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #TSQL as varchar(255)
DECLARE #NewLogin as varchar(50)
SELECT #NewLogin = #Login -- Do your own thing here
SELECT #TSQL = 'ALTER LOGIN [' + #Login + '] WITH NAME=[' + #NewLogin + ']'
PRINT #TSQL
EXEC (#TSQL)
--Whatever else you need to do
FETCH NEXT FROM LoginsCrsr
INTO #Login
END
GO
CLOSE LoginsCrsr;
DEALLOCATE LoginsCrsr;
GO