清理大約 50 台伺服器上的特定域登錄,因此清理使用者
abc
在從一個域遷移到新域之後,特別需要清理 SQL Server 的登錄資訊foo
。遷移後,我們仍然有那些舊的登錄名和使用者,名稱為 abc\login1、abc\login2… 等等。
如何執行此清理以從伺服器中刪除所有登錄名,該伺服器具有 Windows 登錄名,例如域
abc\..
和同一腳本,以刪除所有數據庫實例中的所有相關使用者。我已經搜尋了連結SQL Server 腳本以刪除 Active Directory 中不再存在的帳戶,但它無助於刪除使用者。
注意:這些只是 Windows 登錄名,不屬於任何 AD 組。只想刪除登錄名和與之關聯的數據庫使用者。其他域 AD 使用者已由我們的 AD 和 wintel 團隊照顧,使用者已全部設置好。只是在這裡打掃衛生。
這是一個比你想像的要復雜得多的答案。高級構造非常簡單。按此順序:
- 刪除每個數據庫中與舊
abc
域中的伺服器級登錄關聯的所有數據庫使用者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_date
或modify_date
在sys.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
數據庫主體在數據庫中擁有一個架構,並且不能被刪除。
所以,這裡的重要問題是:
我必須怎麼做才能放棄這個登錄?
您必須首先:
- 將數據庫級使用者控制的所有數據庫級事物轉移到新控制器。
- 刪除數據庫級使用者。
- 將伺服器級登錄控制的所有伺服器級事物轉移到新控制器。
- 刪除伺服器級登錄。
.對於這個帳戶,所有這些都不難。如果您在 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
這並不詳盡。可能還有其他與安全相關的事情可以防止使用者或登錄被丟棄,但這些是我能想到的主要事情。
同樣,如果您有上述場景,您的數據庫中有一個使用者,該使用者映射到伺服器級域登錄,但使用者名不包含域名前綴,您將不得不添加規則來擷取他們。