214

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!

LarsH
  • 27,481
  • 8
  • 94
  • 152
Brett Rigby
  • 6,101
  • 10
  • 46
  • 76
  • 10
    This is an important question, but as phrased, it seems to miss an important distinction: user vs. login. The potential duplicate that Jon linked to really seems to be about users. This question says "user" in the title, but deals with logins in the question code and in the accepted answer. I edited the title and question accordingly. – LarsH May 08 '13 at 15:07
  • 1
    Just to add to the comment by @LarsH, **logins** are associated with a SQL server instance, and **users** are associated with a specific database. Database users can be created from server logins, so they have access to a specific database. See [this excellent article](http://www.sqlservercentral.com/articles/Stairway+Series/109975/) and in fact the whole series it is part of (Stariway to SQL Server Security) – Reversed Engineer Nov 17 '17 at 14:11

11 Answers11

334

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
Derek Morrison
  • 5,456
  • 4
  • 31
  • 24
155

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
Daniel Schilling
  • 4,829
  • 28
  • 60
Johnno Nolan
  • 29,228
  • 19
  • 111
  • 160
  • 8
    you should use QUOTENAME to prevent sql injection. Attacker can pass a @loginName like `x] with password ''y'';\r\ndrop table foo;\r\n` – Remus Rusanu Sep 04 '09 at 14:47
  • 3
    Why was it necessary to create a statement as a string and then use sp_executesql, rather than just directly entering `CREATE LOGIN [@loginName] FROM ...`? Pardon my ignorance, I'd like to learn... – LarsH May 08 '13 at 15:10
  • 6
    @LarsH: Creating the statement as a string is required because CREATE LOGIN cannot use a parameter for the login name, it requires a string literal. Not sure why that is, but I found out the hard way that its true. – Joseph Bongaarts Apr 29 '14 at 20:27
  • @JosephBongaarts: OK, thanks. I guess it's like table names in SELECT statements. Maybe the idea is to decrease the surface area vulnerable to attacks, though I don't know that it would help. – LarsH Apr 30 '14 at 18:38
  • 1
    I think `QUOTENAME()` goes around `@loginName`, not the whole statement, and then you can get rid of the manual [ and ] delimiters around `@loginName`. – brianary Apr 14 '15 at 19:40
  • 1
    the syslogins view is deprecated – Henrik Høyer Nov 22 '19 at 11:47
32

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.

Bomlin
  • 676
  • 4
  • 15
  • Tested, works, and more current than the other answers. +1 to you as well. – David Sep 04 '09 at 14:17
  • Yeah, with 2005 Microsoft took away direct access to the system tables. To keep from breaking old code, they include views that had the same name as the old tables. However, they are only meant for older code and newer code should iuse the new views. In BOL, do a search on Mapping System Tables to find out what you should use. – Bomlin Sep 04 '09 at 14:29
21

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

Hüda
  • 399
  • 3
  • 5
  • This solution is great because it does not require the ALTER ANY LOGIN permission, whereas all the others do. In my situation it wasn't possible to grant our tooling permission to see all logins in the DB, but SUSER_ID('loginName') could still be used to test whether a login already existed. Thanks! – Steve Pick Sep 07 '21 at 16:20
20

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

Henrik Høyer
  • 1,225
  • 1
  • 19
  • 27
8

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
Marc
  • 1,798
  • 8
  • 15
7

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

Vinicius
  • 1,601
  • 19
  • 19
6

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.

David
  • 72,686
  • 18
  • 132
  • 173
5

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
Akshita
  • 849
  • 8
  • 15
0

Starting SQL 2016:

DROP USER IF EXISTS [userName]

CREATE USER [userName] FOR LOGIN [loginName]
dbc
  • 104,963
  • 20
  • 228
  • 340
Albert Zakhia
  • 99
  • 1
  • 6
  • Just a note that whilst this answer maybe useful in relation to this question - the question did specify LOGIN not USER – Chris Nevill Jun 01 '22 at 09:36
-1

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
PM 77-1
  • 12,933
  • 21
  • 68
  • 111
M.Alaghemand
  • 105
  • 2
  • 6
  • 1
    I dunno - saying that "you have to check login existence using syslogins view", then posting code that doesn't use that view looks like a copy and paste issue. Also, after the first statement, the line "Then you have to check your database existence", using parallel form, looks like you are asking someone to check for the existence of a database, not a DB level user. And you need to specify that the second batch needs to be run inside the target DB. Overall, this is just a very poor explanation. And since you added it five years after the highest upvoted answer said the same, but better... – Laughing Vergil Aug 02 '17 at 20:53