Sql-Server

清理大約 50 台伺服器上的特定域登錄,因此清理使用者

  • September 6, 2018

abc在從一個域遷移到新域之後,特別需要清理 SQL Server 的登錄資訊foo

遷移後,我們仍然有那些舊的登錄名和使用者,名稱為 abc\login1、abc\login2… 等等。

如何執行此清理以從伺服器中刪除所有登錄名,該伺服器具有 Windows 登錄名,例如域abc\..和同一腳本,以刪除所有數據庫實例中的所有相關使用者。

我已經搜尋了連結SQL Server 腳本以刪除 Active Directory 中不再存在的帳戶,但它無助於刪除使用者。

注意:這些只是 Windows 登錄名,不屬於任何 AD 組。只想刪除登錄名和與之關聯的數據庫使用者。其他域 AD 使用者已由我們的 AD 和 wintel 團隊照顧,使用者已全部設置好。只是在這裡打掃衛生。

這是一個比你想像的要復雜得多的答案。高級構造非常簡單。按此順序:

  1. 刪除每個數據庫中與舊abc域中的伺服器級登錄關聯的所有數據庫使用者
  2. abc從舊域中刪除所有伺服器級登錄

必須按照這個順序- 任何一個步驟

$$ all the database users from all the databases $$然後一步$$ all the logins $$,或重複的步驟$$ each login’s database user $$進而$$ each login $$. 一種方法是使用動態 SQL,但是嵌套的單引號很快就很難看。要刪除每個數據庫中的使用者:

DECLARE @innersql nvarchar(max) = N'PRINT DB_NAME();
 DECLARE @sql nvarchar(max) = N''''; 
 SELECT @sql += N''
     DROP USER '' + QUOTENAME(name) + '';'' 
   FROM sys.database_principals 
   WHERE name LIKE N''abc\%''; 
 PRINT @sql;
 --EXEC sys.sp_executesql @sql;'; -- happy? uncomment

DECLARE @db sysname, @exec nvarchar(max);

DECLARE c CURSOR FOR SELECT name FROM sys.databases;

OPEN c;

FETCH NEXT FROM c INTO @db;

WHILE @@FETCH_STATUS <> -1
BEGIN
 SET @exec = QUOTENAME(@db) + N'.sys.sp_executesql';
 EXEC @exec @innersql;
 FETCH NEXT FROM c INTO @db;
END

CLOSE c;
DEALLOCATE c;
GO

然後在伺服器級別刪除登錄:

DECLARE @sql nvarchar(max) = N'';

SELECT @sql += N'
DROP LOGIN ' + QUOTENAME(name) + N';' 
 FROM sys.server_principals
 WHERE name LIKE N'NT Service\%';

PRINT @sql;
-- EXEC sys.sp_executesql @sql; -- happy? uncomment

當然,也有並發症。

對於某些或所有使用者/登錄,您可能會發現上述命令失敗。原因可能會有所不同。例如:

  • 如果數據庫使用者是從 Windows 登錄創建的,但沒有domain\login約定怎麼辦?例如:
USE yourdb;
GO
CREATE USER splunge FROM LOGIN [abc\login1];

在這種情況下,由於伺服器級登錄不必首先存在,因此您無法判斷該登錄是來自域abc還是foo. 所以,這裡的重要問題是:

**這個數據庫使用者可以安全刪除嗎?**也許您可以根據create_datemodify_datesys.database_principals別名使用者名中的某些模式或外部文件中確定這一點。

  • 如果登錄擁有/控制對象怎麼辦?模式?工作?數據庫?例子:
USE master;
GO 
CREATE LOGIN [abc\login2] FROM WINDOWS;
CREATE LOGIN [abc\login3] FROM WINDOWS;
GO
ALTER AUTHORIZATION ON DATABASE::yourdb TO [abc\login2];
GO
EXEC msdb.dbo.sp_update_job @job_name = N'syspolicy_purge_history', 
 @owner_login_name = N'abc\login2';

USE yourdb;
GO
CREATE USER [abc\login3] FROM LOGIN [abc\login3];
GO
CREATE SCHEMA blat AUTHORIZATION [abc\login3];

所有這些都可以阻止嘗試刪除數據庫使用者或伺服器級登錄。我不能login2從伺服器中刪除,因為他們擁有一個數據庫和一份工作。我不能login3從數據庫中刪除,因為他們擁有一個模式。

當我嘗試刪除登錄時abc\login2

消息 15174,級別 16,狀態 1

登錄 ‘abc\login2’ 擁有一個或多個數據庫。在刪除登錄之前更改數據庫的所有者。

如果登錄名不擁有數據庫但擁有工作:

消息 15170,級別 16,狀態 1

此登錄名是 1 個工作的所有者。您必須先刪除或重新分配這些作業,然後才能刪除登錄。

如果我嘗試刪除使用者abc\login3

消息 15138,級別 16,狀態 1

數據庫主體在數據庫中擁有一個架構,並且不能被刪除。

所以,這裡的重要問題是:

我必須怎麼做才能放棄這個登錄?

您必須首先:

  1. 將數據庫級使用者控制的所有數據庫級事物轉移到新控制器。
  2. 刪除數據庫級使用者。
  3. 將伺服器級登錄控制的所有伺服器級事物轉移到新控制器。
  4. 刪除伺服器級登錄。

.對於這個帳戶,所有這些都不難。如果您在 50 或 100 個數據庫中擁有 50 或 100 個這樣的數據庫怎麼辦?

我們需要擴展我們的腳本。

首先,在我們從數據庫中刪除使用者之前,我們需要將他們的任何模式切換到其他人(為了簡單起見,我們假設一下dbo):

DECLARE @innersql nvarchar(max) = N'PRINT DB_NAME();
 DECLARE @sql nvarchar(max) = N''''; 

 SELECT @sql += N''
     ALTER AUTHORIZATION ON SCHEMA::'' + s.name + '' TO sa;''
   FROM sys.schemas AS s
   INNER JOIN sys.database_principals AS dp
   ON s.principal_id = dp.principal_id
   WHERE dp.name LIKE N''abc\%'';

 SELECT @sql += N''
     DROP USER '' + QUOTENAME(name) + '';'' 
   FROM sys.database_principals 
   WHERE name LIKE N''abc\%''; 

 PRINT @sql;
 --EXEC sys.sp_executesql @sql;'; -- happy? uncomment

DECLARE @db sysname, @exec nvarchar(max);

DECLARE c CURSOR FOR SELECT name FROM sys.databases;

OPEN c;

FETCH NEXT FROM c INTO @db;

WHILE @@FETCH_STATUS <> -1
BEGIN
 SET @exec = QUOTENAME(@db) + N'.sys.sp_executesql';
 EXEC @exec @innersql;
 FETCH NEXT FROM c INTO @db;
END

CLOSE c;
DEALLOCATE c;
GO

對於登錄,我們需要將數據庫和作業轉移給不同的所有者,假設sa(再次,為簡單起見):

DECLARE @sql nvarchar(max) = N'';

SELECT @sql += N'
   ALTER AUTHORIZATION ON DATABASE::' + QUOTENAME(db.name) + N' TO sa;'
 FROM sys.databases AS db
 INNER JOIN sys.server_principals AS sp
 ON sp.sid = db.owner_sid
 WHERE sp.name LIKE N'NT Service%';

SELECT N'
   EXEC msdb.dbo.sp_update_job @job_name = N''' 
     + REPLACE(jobs.name, '''', '''''') + N''',
     @owner_login_name = N''sa'';'
 FROM msdb.dbo.sysjobs AS jobs
 INNER JOIN sys.server_principals AS sp
 ON sp.sid = jobs.owner_sid
 WHERE sp.name LIKE N'NT Service%';

SELECT @sql += N'
DROP LOGIN ' + QUOTENAME(name) + N';' 
 FROM sys.server_principals
 WHERE name LIKE N'NT Service\%';

PRINT @sql;
-- EXEC sys.sp_executesql @sql; -- happy? uncomment

這並不詳盡。可能還有其他與安全相關的事情可以防止使用者或登錄被丟棄,但這些是我能想到的主要事情。

同樣,如果您有上述場景,您的數據庫中有一個使用者,該使用者映射到伺服器級域登錄,但使用者名不包含域名前綴,您將不得不添加規則來擷取他們。

引用自:https://dba.stackexchange.com/questions/216670