Recently, I have created an AWS RDS SQL Server database (SQL Server 2017 Express Edition, v14.00.3281.6.v1 using free tier templates). Then I can use the master user name and password to login the database server, and I can create database/login, but I cannot execute
EXEC sp_changedbowner 'testLogin'
to change the dba to the new-created login testLogin, I get an error
SQL Error [15151] [S0001]: Cannot find the principal 'testlogin', because it does not exist or you do not have permission.
The following is the whole SQL script I run. It seems that the master user doesn't have enough permission? Could anyone tell me how to fix it or how to make the script run successfully. BTW, the same script runs successfully in a traditional SQL Server database.
Thanks very much.
SET IMPLICIT_TRANSACTIONS OFF
USE master;
-- Get the SQL Server data path
DECLARE #data_path nvarchar(256)
SET #data_path = (SELECT SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
FROM master.sys.master_files
WHERE database_id = 1 AND file_id = 1)
DECLARE #model_data_size_kb int
SET #model_data_size_kb = (SELECT SUM(size) * 8
FROM master.sys.master_files
WHERE database_id = 3 and [type] = 0)
IF #model_data_size_kb < (250 * 1024)
SET #model_data_size_kb = 250 * 1024
DECLARE #model_log_size_kb int
SET #model_log_size_kb = (SELECT SUM(size) * 8
FROM master.sys.master_files
WHERE database_id = 3 and [type] = 1)
IF #model_log_size_kb < (50 * 1024)
SET #model_log_size_kb = 50 * 1024
DECLARE #schema_data_size_str nvarchar(12)
DECLARE #schema_log_size_str nvarchar(12)
SET #schema_data_size_str = CAST(#model_data_size_kb AS nvarchar(12))
SET #schema_log_size_str = CAST(#model_log_size_kb AS nvarchar(12))
-- execute the CREATE DATABASE statement
EXECUTE ('CREATE DATABASE testdb
ON
( NAME = testdb_dat1,
FILENAME = '''+ #data_path + 'testdb.mdf'',
SIZE = ' + #schema_data_size_str + 'KB,
MAXSIZE = UNLIMITED,
FILEGROWTH = 25MB )
LOG ON
( NAME = testdb_log1,
FILENAME = '''+ #data_path + 'testdb.ldf'',
SIZE = ' + #schema_log_size_str + 'KB,
MAXSIZE = 10GB,
FILEGROWTH = 5MB )
COLLATE SQL_Latin1_General_CP1_CS_AS'
);
GO
ALTER DATABASE testdb SET READ_COMMITTED_SNAPSHOT ON;
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE type = 'S' and name = N'testlogin')
BEGIN
CREATE LOGIN testlogin WITH PASSWORD='testlogin', DEFAULT_DATABASE=testdb, CHECK_POLICY = OFF
END
USE testdb;
EXEC sp_changedbowner 'testlogin'; -- this statement reports exception
After research, I found that AWS RDS SqlServer doesn't support add more permission to master user, which means that it can not execute "sp_changedbowner" since it require "Take Ownship" permission on the database and requires the new owner has "impersonate" permission on the login or "control server" permission on the server. click this for more detail about sp_changedbowner
That is we cannot change db owner to others in RDS SqlServer, it is always a built-in "rdsa" user, but we can use ALTER ROLE db_owner ADD MEMBER testlogin to add user to the db_owner role, so that it can perform any database actions.
So I changed my script to the following and works well:
-- The above is the same
USE testdb;
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE type = 'S' and name = N'testlogin')
BEGIN
CREATE LOGIN testlogin WITH PASSWORD='testlogin', DEFAULT_DATABASE=testdb, CHECK_POLICY = OFF
CREATE USER testlogin FOR LOGIN testlogin
END
ALTER ROLE db_owner ADD MEMBER testlogin
Related
I have a user under SQL Server -> Security-> Logins as 'testuser'.
I create a database , TestDB and attempt to create a user in the TestDB with name 'testuser', but get the following error :
Msg 15063, Level 16, State 1, Line 11
The login already has an account under a different user name.
Msg 15410, Level 11, State 1, Procedure sys.sp_addrolemember, Line 35 [Batch Start Line 0]
User or role 'testuser' does not exist in this database.
This is the create script I use:
USE [TestDB]
IF NOT EXISTS (SELECT loginname
FROM master.dbo.syslogins
WHERE name = 'testuser')
BEGIN
CREATE LOGIN [testuser]
WITH PASSWORD = 'testpassword',
DEFAULT_DATABASE = TestDB,
DEFAULT_LANGUAGE = [us_english],
CHECK_EXPIRATION = OFF,
CHECK_POLICY = OFF
END
USE TestDB;
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = 'testuser')
BEGIN
CREATE USER [testuser] FOR LOGIN [testuser]
WITH DEFAULT_SCHEMA = [dbo]
END
EXEC sys.sp_addrolemember
#rolename = N'db_owner', #membername = N'testuser'
I assume I should be able to create this user as it is in the database level ?
The error message is clear , you already have a username ( which can be anything ) in TestDB database which is linked to the same Login name , so you can't make multiple user under one login.
to find out what is that username you can use query below:
USE TestDB
Select u.name , loginname
from sys.syslogins l
inner join sys.sysusers u
on l.sid = u.sid
where loginname = 'testuser'
Running SQL Server 2014 Express. Logged on as sa I try to execute the code below. It gives me the following error:
Msg 15247, Level 16, State 1, Line 25 User does not have permission to
perform this action.
Why?! If I select SYSTEM_USER inside the procedure, it is indeed sa (the rightful owner).
USE [MyDatabase]
GO
CREATE PROCEDURE [dbo].[create_login]
#Login [nvarchar](256),
#Password [nvarchar](128)
WITH EXECUTE AS OWNER
AS
BEGIN
SET NOCOUNT ON
DECLARE #Sql NVARCHAR(4000)
SET #sql = N'CREATE LOGIN ' + QUOTENAME(#Login) + N' WITH PASSWORD = '
+ QUOTENAME(#Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;'
EXEC (#sql)
END
GO
GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role]
GO
-- Let's go!
EXEC [dbo].[create_login] N'NewUser', N'c0Mpl3xP#55w0rd'
GO
-- Error! :(
If I run the statements outside the stored procedure, it works.
I'm trying to make a stored procedure that allows regular users to add server logins. The above does not seem to work. Please advise!
A login is a server level object and therefore requires server level permissions. EXECUTE AS <database-user> is a database-scoped security context.
One way to grant normal users privileged operations via stored procedures is by signing the module with a certificate that maps to a login that has the necessary rights. The necessary steps in this case are:
Create a certificate in the master database
Create a login for that certificate
Grant the certificate login rights to create logins
Export the certificate from master
Import the certificate into the application database
Sign the stored procedure with the certificate
Below is an example gleaned from Erland Sommarskog's web site. Note that you will need to resign the proc with the cert each time it is altered.
--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
CREATE MASTER KEY ENCRYPTION BY PASSWORD='M#sterkEEPassw0rd';
END;
GO
CREATE CERTIFICATE SecurityAdministratorCertificate
WITH
SUBJECT = 'Allows non-privileged users to create and alter logins'
, START_DATE = '20020101'
, EXPIRY_DATE = '20300101';
GO
CREATE LOGIN SecurityAdministratorCertificateLogin
FROM CERTIFICATE SecurityAdministratorCertificate;
GO
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO
--export cert from master
DECLARE #CERTENC VARBINARY(MAX);
DECLARE #CERTPVK VARBINARY(MAX);
SELECT #CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT #CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
'All you need is love');
DECLARE #sql nvarchar(MAX);
SELECT #sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
+ CONVERT(nvarchar(MAX), #CERTENC, 1)
+ ' WITH PRIVATE KEY ( BINARY = '
+ CONVERT(nvarchar(MAX), #CERTPVK, 1)
+ ', DECRYPTION BY PASSWORD = ''All you need is love'');'
--import cert into app databases
USE MyDatabase;
--create database master key, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
CREATE MASTER KEY ENCRYPTION BY PASSWORD='M#sterkEEPassw0rd';
END;
EXEC(#sql);
GO
CREATE PROCEDURE [dbo].[create_login]
#Login [nvarchar](256),
#Password [nvarchar](128)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(4000);
SET #sql = N'CREATE LOGIN ' + QUOTENAME(#Login) + N' WITH PASSWORD = '
+ QUOTENAME(#Password, N'''') + N',CHECK_EXPIRATION=OFF, CHECK_POLICY=ON;';
EXEC (#sql);
END
GO
--grant exec permissions to users
GRANT EXECUTE ON [dbo].[create_login] TO [my_simple_role];
GO
--sign proc with certificate
ADD SIGNATURE TO dbo.create_login BY CERTIFICATE SecurityAdministratorCertificate;
GO
EDIT:
The above example encrypts the certificate private key using the database master key rather than a certificate-specific password. In a scenario where the database is restored or attached to a different SQL instance (without also restoring the master database), you'll need to recreate server level
objects in the master database, including all logins needed by the application and certificates stored in master. To recover the certificate, one method is to copy the cert from the user database to master after the restore/attach and then recreate the certificate login with permissions. The DMK cannot be opened automatically in this case because the service master key, which encrypts the database master key, is different on the new instance. The original password is needed to open the DMK manually in the script that copies the cert into the master database. The certificate password used to copy the cert between databases is temporary and need not be retained.
Here's and example to recreate the cert and cert login in master after a restore or attach:
USE MyDatabase;
--open DMK with original password
OPEN MASTER KEY DECRYPTION BY PASSWORD='M#sterkEEPassw0rd';
--export cert from user database
USE MyDatabase;
DECLARE #CERTENC VARBINARY(MAX);
DECLARE #CERTPVK VARBINARY(MAX);
SELECT #CERTENC = CERTENCODED(CERT_ID(N'SecurityAdministratorCertificate'));
SELECT #CERTPVK = CERTPRIVATEKEY(CERT_ID(N'SecurityAdministratorCertificate'),
'temporary password here');
DECLARE #sql nvarchar(MAX);
SELECT #sql = N'CREATE CERTIFICATE SecurityAdministratorCertificate FROM BINARY = '
+ CONVERT(nvarchar(MAX), #CERTENC, 1)
+ ' WITH PRIVATE KEY ( BINARY = '
+ CONVERT(nvarchar(MAX), #CERTPVK, 1)
+ ', DECRYPTION BY PASSWORD = ''temporary password here'');'
SELECT #sql
CLOSE MASTER KEY;
--import cert into master
USE master;
--create database master key in new instance master database, if necessary
IF NOT EXISTS(SELECT 1 FROM sys.symmetric_keys WHERE name = N'##MS_DatabaseMasterKey##')
BEGIN
CREATE MASTER KEY ENCRYPTION BY PASSWORD='M#sterkEEPassw0rd';
END;
EXEC(#sql);
GO
--recreate login and assign permissions
CREATE LOGIN SecurityAdministratorCertificateLogin
FROM CERTIFICATE SecurityAdministratorCertificate;
GRANT ALTER ANY LOGIN TO SecurityAdministratorCertificateLogin;
GO
When I try to install tSQLt onto an existing database i get the following error:
The database owner SID recorded in the master database differs from
the database owner SID recorded in database ''. You should correct
this situation by resetting the owner of database '' using the ALTER
AUTHORIZATION statement.
This problem can arise when a database restored from a backup and the SID of the database owner does not match the owners SID listed in the master database. Here is a solution that uses the "ALTER AUTHORIZATION" statement recommended in the error message:
DECLARE #Command VARCHAR(MAX) = 'ALTER AUTHORIZATION ON DATABASE::[<<DatabaseName>>] TO
[<<LoginName>>]'
SELECT #Command = REPLACE(REPLACE(#Command
, '<<DatabaseName>>', SD.Name)
, '<<LoginName>>', SL.Name)
FROM master..sysdatabases SD
JOIN master..syslogins SL ON SD.SID = SL.SID
WHERE SD.Name = DB_NAME()
PRINT #Command
EXEC(#Command)
Added this to the top of the tSQLt.class.sql script
declare #user varchar(50)
SELECT #user = quotename(SL.Name)
FROM master..sysdatabases SD inner join master..syslogins SL
on SD.SID = SL.SID
Where SD.Name = DB_NAME()
exec('exec sp_changedbowner ' + #user)
Apply the below script on database you get the error:
EXEC sp_changedbowner 'sa'
ALTER DATABASE [database_name] SET TRUSTWORTHY ON
The simplest way to change DB owner is:
EXEC SP_ChangeDBOwner 'sa'
Necromaning:
If you don't want to use the SQL-Server 2000 views (deprecated), use this:
-- Restore sid when db restored from backup...
DECLARE #Command NVARCHAR(MAX)
SET #Command = N'ALTER AUTHORIZATION ON DATABASE::<<DatabaseName>> TO <<LoginName>>'
SELECT #Command = REPLACE
(
REPLACE(#Command, N'<<DatabaseName>>', QUOTENAME(SD.Name))
, N'<<LoginName>>'
,
QUOTENAME
(
COALESCE
(
SL.name
,(SELECT TOP 1 name FROM sys.server_principals WHERE type_desc = 'SQL_LOGIN' AND is_disabled = 'false' ORDER BY principal_id ASC )
)
)
)
FROM sys.databases AS SD
LEFT JOIN sys.server_principals AS SL
ON SL.SID = SD.owner_sid
WHERE SD.Name = DB_NAME()
PRINT #command
EXECUTE(#command)
GO
Also prevents bug on oddly named database or user, and also fixes bug if no user is associated (uses sa login).
I have an MS SQL 2000 database that was backed up from a public server and restored at a test location for an upgrade test. The problem is that the user that had access permission on the public server does not exist on the testing server, and now all tables are prefixed with that username (which requires ALL queries against those tables to be changed!)
Is there any quick way to fix this? I have changed the database owner but this did not help.
Create the login and users, but find out the SID from sysusers
EXEC sp_addlogin 'TheLogin', 'ThePassword', #sid = ???
EXEC sp_adduser 'TheLogin','TheUser'
Note: SQL Server 2000 so can't use CREATE LOGIN or CREATE USER
Ok, found the answer - the OBJECT owner must be changed to DBO, negating the need to prefix references to your object in your SQL scripts/queries - the object in this case being the database table(s)
Here is a script that will change the owner for objects within a database (not my own code)
DECLARE #currentObject nvarchar(517)
DECLARE #qualifiedObject nvarchar(517)
DECLARE #currentOwner varchar(50)
DECLARE #newOwner varchar(50)
SET #currentOwner = 'old_owner'
SET #newOwner = 'dbo'
DECLARE alterOwnerCursor CURSOR FOR
SELECT [name] FROM dbo.sysobjects
WHERE xtype = 'U' or xtype = 'P'
AND LEFT([name], 2) <> 'dt'
OPEN alterOwnerCursor
FETCH NEXT FROM alterOwnerCursor INTO #currentObject
WHILE ##FETCH_STATUS = 0
BEGIN
SET #qualifiedObject = CAST(#currentOwner as varchar) + '.' + CAST(#currentObject as varchar)
EXEC sp_changeobjectowner #qualifiedObject, #newOwner
FETCH NEXT FROM alterOwnerCursor INTO #currentObject
END
CLOSE alterOwnerCursor
DEALLOCATE alterOwnerCursor
I need to check if a specific login already exists on the SQL Server, and if it doesn't, then I need to add it.
I have found the following code to actually add the login to the database, but I want to wrap this in an IF statement (somehow) to check if the login exists first.
CREATE LOGIN [myUsername] WITH PASSWORD=N'myPassword',
DEFAULT_LANGUAGE=[us_english],
CHECK_EXPIRATION=OFF,
CHECK_POLICY=OFF
GO
I understand that I need to interrogate a system database, but not sure where to start!
Here's a way to do this in SQL Server 2005 and later without using the deprecated syslogins view:
IF NOT EXISTS
(SELECT name
FROM master.sys.server_principals
WHERE name = 'LoginName')
BEGIN
CREATE LOGIN [LoginName] WITH PASSWORD = N'password'
END
The server_principals view is used instead of sql_logins because the latter doesn't list Windows logins.
If you need to check for the existence of a user in a particular database before creating them, then you can do this:
USE your_db_name
IF NOT EXISTS
(SELECT name
FROM sys.database_principals
WHERE name = 'Bob')
BEGIN
CREATE USER [Bob] FOR LOGIN [Bob]
END
From here
If not Exists (select loginname from master.dbo.syslogins
where name = #loginName and dbname = 'PUBS')
Begin
Select #SqlStatement = 'CREATE LOGIN ' + QUOTENAME(#loginName) + '
FROM WINDOWS WITH DEFAULT_DATABASE=[PUBS], DEFAULT_LANGUAGE=[us_english]')
EXEC sp_executesql #SqlStatement
End
As a minor addition to this thread, in general you want to avoid using the views that begin with sys.sys* as Microsoft is only including them for backwards compatibility. For your code, you should probably use sys.server_principals. This is assuming you are using SQL 2005 or greater.
You can use the built-in function:
SUSER_ID ( [ 'myUsername' ] )
via
IF [value] IS NULL [statement]
like:
IF SUSER_ID (N'myUsername') IS NULL
CREATE LOGIN [myUsername] WITH PASSWORD=N'myPassword',
DEFAULT_LANGUAGE=[us_english],
CHECK_EXPIRATION=OFF,
CHECK_POLICY=OFF
GO
https://technet.microsoft.com/en-us/library/ms176042(v=sql.110).aspx
In order to hande naming conflict between logins, roles, users etc. you should check the type column according to Microsoft sys.database_principals documentation
In order to handle special chacters in usernames etc, use N'<name>' and [<name>] accordingly.
Create login
USE MASTER
IF NOT EXISTS (SELECT 1 FROM master.sys.server_principals WHERE
[name] = N'<loginname>' and [type] IN ('C','E', 'G', 'K', 'S', 'U'))
CREATE LOGIN [<loginname>] <further parameters>
Create database user
USE [<databasename>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE
[name] = N'<username>' and [type] IN ('C','E', 'G', 'K', 'S', 'U'))
CREATE USER [<username>] FOR LOGIN [<loginname>]
Create database role
USE [<databasename>]
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE
[name] = N'<rolename>' and Type = 'R')
CREATE ROLE [<rolename>]
Add user to role
USE [<databasename>]
EXEC sp_addrolemember N'<rolename>', N'<username>'
Grant rights to role
USE [<databasename>]
GRANT SELECT ON [<tablename>] TO [<rolename>]
GRANT UPDATE ON [<tablename>] ([<columnname>]) TO [<rolename>]
GRANT EXECUTE ON [<procedurename>] TO [<rolename>]
The SQL is tested on SQL Server 2005, 2008, 2008 R2, 2014, 2016, 2017, 2019
Try this (replace 'user' with the actual login name):
IF NOT EXISTS(
SELECT name
FROM [master].[sys].[syslogins]
WHERE NAME = 'user')
BEGIN
--create login here
END
This is for Azure SQL:
IF (EXISTS(SELECT TOP 1 1 FROM sys.sql_logins WHERE [name] = '<login>'))
DROP LOGIN [<login>];
Source: How to check whether database user already exists in Azure SQL Database
This works on SQL Server 2000.
use master
select count(*) From sysxlogins WHERE NAME = 'myUsername'
on SQL 2005, change the 2nd line to
select count(*) From syslogins WHERE NAME = 'myUsername'
I'm not sure about SQL 2008, but I'm guessing that it will be the same as SQL 2005 and if not, this should give you an idea of where t start looking.
what are you exactly want check for login or user ?
a login is created on server level and a user is created at database level so a login is unique in server
also a user is created against a login, a user without login is an orphaned user and is not useful as u cant carry out sql server login without a login
maybe u need this
check for login
select 'X' from master.dbo.syslogins where loginname=<username>
the above query return 'X' if login exists else return null
then create a login
CREATE LOGIN <username> with PASSWORD=<password>
this creates a login in sql server .but it accepts only strong passwords
create a user in each database you want to for login as
CREATE USER <username> for login <username>
assign execute rights to user
GRANT EXECUTE TO <username>
YOU MUST HAVE SYSADMIN permissions or say 'sa' for short
you can write a sql procedure for that on a database
create proc createuser
(
#username varchar(50),
#password varchar(50)
)
as
begin
if not exists(select 'X' from master.dbo.syslogins where loginname=#username)
begin
if not exists(select 'X' from sysusers where name=#username)
begin
exec('CREATE LOGIN '+#username+' WITH PASSWORD='''+#password+'''')
exec('CREATE USER '+#username+' FOR LOGIN '+#username)
exec('GRANT EXECUTE TO '+#username)
end
end
end
Starting SQL 2016:
DROP USER IF EXISTS [userName]
CREATE USER [userName] FOR LOGIN [loginName]
First you have to check login existence using syslogins view:
IF NOT EXISTS
(SELECT name
FROM master.sys.server_principals
WHERE name = 'YourLoginName')
BEGIN
CREATE LOGIN [YourLoginName] WITH PASSWORD = N'password'
END
Then you have to check your database existence:
USE your_dbname
IF NOT EXISTS
(SELECT name
FROM sys.database_principals
WHERE name = 'your_dbname')
BEGIN
CREATE USER [your_dbname] FOR LOGIN [YourLoginName]
END