sp_help_revlogin causing loss of characters on LOGINs with Unicode characters
I'll preface this by saying that it's much better to do this via some tool, rather than cobbling together something in T-SQL. You can use SSMS or similar.
Be that as it may:
The script you have posted has multiple issues.
-
The primary issue you were facing: use of
varchar
instead ofnvarchar
, means that other languages get clobbered. - Use of a cursor unnecessarily. This can be done in a single query using string aggregation.
- The binary-to-hex procedure is complete overkill, as this can be done with a simple
CONVERT
. Even if it were necessary, it could have been done with an inline TVF. - Some missing
QUOTENAME
usages, to quote the names correctly. - It only adds fixed server roles, rather than any user-defined roles
- It only adds
CONNECT
permissions, rather than any other permissions. - Usage of the deprecated
sys.syslogins
, which was replaced in 2005.
The following script is a full rewrite.
- It returns all SQL logins and Windows logins based on AD users or AD groups (but not any other logins such as certificate based ones).
- It adds all server-level permissions.
- It adds all server roles, including user-defined.
CREATE OR ALTER PROCEDURE [dbo].[sp_help_revlogin]
@login_name sysname = NULL
AS
SELECT
'
/* sp_help_revlogin script
** Generated ' + CONVERT(nvarchar(30), GETDATE()) + ' on ' + QUOTENAME(@@SERVERNAME) + ' */
GO
' + STRING_AGG(CONCAT(
'
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N',
QUOTENAME(p.name, ''''),
')
BEGIN
CREATE LOGIN ',
QUOTENAME(p.name),
CASE WHEN p.type IN ('G', 'U') THEN ' FROM WINDOWS' END,
' WITH',
CASE WHEN p.type = 'S' THEN CONCAT(
' PASSWORD = ',
CONVERT(nvarchar(max), ISNULL(l.password_hash, 0x0), 1),
' HASHED, SID = ',
CONVERT(nvarchar(max), p.sid, 1),
CASE WHEN l.is_policy_checked = 0 THEN ', CHECK_POLICY = OFF' END,
CASE WHEN l.is_expiration_checked = 1 THEN ', CHECK_EXPIRATION = ON' END,
','
) END,
' DEFAULT_DATABASE = ',
QUOTENAME(p.default_database_name),
', DEFAULT_LANGUAGE = ',
QUOTENAME(p.default_language_name),
';
END;
GO
',
CASE WHEN p.is_disabled = 1 THEN
'ALTER LOGIN ' + QUOTENAME(p.name) + ' DISABLE;
GO
'
END,
r.roles + '
GO
',
sp.perms + '
GO
'
), N'
') WITHIN GROUP (ORDER BY p.name)
FROM sys.server_principals p
LEFT JOIN sys.sql_logins l ON l.principal_id = p.principal_id
CROSS APPLY (
SELECT STRING_AGG(
'EXEC sp_addsrvrolemember @loginame = ' + p2.name + ', @rolename = ' + QUOTENAME(r.name, '''') + ';'
, '
GO
'
)
FROM sys.server_role_members rm
JOIN sys.server_principals r ON r.principal_id = rm.role_principal_id
CROSS APPLY (VALUES( QUOTENAME(p.name, '''') )) p2(name) -- error 8124
WHERE rm.member_principal_id = p.principal_id
) r(roles)
CROSS APPLY (
SELECT STRING_AGG(CONCAT(
CASE WHEN sp.state = 'W' THEN N'GRANT' ELSE sp.state_desc END,
' ',
sp.permission_name COLLATE DATABASE_DEFAULT,
' TO ',
p2.name,
CASE WHEN sp.state = 'W' THEN N' WITH GRANT OPTION' END,
';'
), N'
GO
'
)
FROM sys.server_permissions sp
CROSS APPLY (VALUES( QUOTENAME(p.name, '''') )) p2(name) -- error 8124
WHERE sp.grantee_principal_id = p.principal_id
AND sp.class = 100
) sp(perms)
WHERE p.type IN ('S', 'G', 'U')
AND p.name <> 'sa'
AND (p.name = @login_name OR @login_name IS NULL);
GO
db<>fiddle