用於刪除 Active Directory 中不再存在的帳戶的 SQL Server 腳本
我們有一個很快將遷移到 SQL Server 2005 的 SQL Server 2000。它創建了多年的 Windows 身份驗證帳戶,但在 Active Directory 中不再存在,這會阻止複制數據庫嚮導在新伺服器上創建這些帳戶。
是否有腳本或某種自動方式來刪除我們的 Active Directory 中不再存在的帳戶?
編輯:為了清楚起見,需要刪除的登錄名在 SQL Server 2000 上,它不支持該
DROP LOGIN
命令。另外,手動刪除 SQL Server 2000 中的登錄名(我認為)可以完成,
exec sp_droplogin 'loginname'
但在我的情況下,無論我使用“域\登錄名”還是“登錄名”,都找不到登錄名只是為了增加混亂,
exec sp_revokelogin 'domain\loginname'
似乎確實有效。編輯2:終於解決了這個問題。許多有問題的登錄以程式方式添加到數據庫中,雖然它們在使用者可以連接的意義上工作,但當 SQL Server 不期望域時,使用者名與 NT 登錄名的域前綴登錄不匹配,反之亦然反之亦然。
為了解決這個問題,我修改了 sp_droplogin 過程以取消其中一項出錯的檢查。
我接受我自己的答案,因為它適用於 SQL Server 2000。
我最後做的是列出以下帳戶:
exec sp_validatelogins
並執行
exec sp_dropuser loginname exec sp_droplogin loginname
在結果上。
根據我的原始評論,該
SUSER_SID
函式似乎只是獲取創建登錄時記錄的任何 sid,並且實際上並不查詢 Active Directory(有道理,因為這可能很昂貴——我什至嘗試重新啟動伺服器服務)。這是一個完成該任務的 C# 控制台應用程序,允許您在實際刪除之前審核將要刪除的登錄。
這個應用程序需要 .NET 3.5 或更高版本才能執行,理論上它可以放入 PowerShell 腳本中(我更喜歡直接程式)。
要從伺服器中刪除本地/機器使用者帳戶的任何登錄,您需要在伺服器機器上執行此應用程序*,*並對
ContextType
變數進行硬編碼(我有它用於在我的未加入域的家用電腦上進行測試)。否則,您可以從與伺服器位於同一域中的任何電腦上執行它,該電腦也可以訪問伺服器。在將參數外部化並稍微清理一下程式碼之後,我將在我的部落格上發布這個,所以當我這樣做時,我將編輯這篇文章。但這會讓你現在就開始。
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.DirectoryServices.AccountManagement; using System.Security.Principal; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { string connectionString = @"Data Source=.\SQL2008R2DEV;Initial Catalog=master;Integrated Security=SSPI;"; ContextType domainContext = Environment.UserDomainName == Environment.MachineName ? ContextType.Machine : ContextType.Domain; IList<string> deletedPrincipals; using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); deletedPrincipals = _GetDeletedPrincipalsFromServer(conn, domainContext); } if (deletedPrincipals.Count > 0) { Console.WriteLine("Logins that will be dropped:"); foreach (string loginName in deletedPrincipals) Console.WriteLine(loginName); Console.WriteLine(); Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } else Console.WriteLine("No logins with deleted principals."); if (deletedPrincipals.Count > 0) { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); _DropDeletedPrincipalLoginsFromServer(conn, deletedPrincipals); } Console.WriteLine("Logins dropped successfully."); } Console.WriteLine(); Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } private static void _DropDeletedPrincipalLoginsFromServer(IDbConnection conn, IList<string> loginNames) { if (loginNames.Count == 0) return; StringBuilder sb = new StringBuilder(); foreach (string loginName in loginNames) sb.AppendFormat("DROP LOGIN {0};", loginName); // This was escaped on the way out of SQL Server IDbTransaction transaction = conn.BeginTransaction(); IDbCommand cmd = conn.CreateCommand(); cmd.Transaction = transaction; cmd.CommandText = sb.ToString(); try { cmd.ExecuteNonQuery(); transaction.Commit(); } catch { try { transaction.Rollback(); } catch { } throw; } } private static IList<string> _GetDeletedPrincipalsFromServer(IDbConnection conn, ContextType domainContext) { List<string> results = new List<string>(); IDbCommand cmd = conn.CreateCommand(); cmd.CommandText = "SELECT sid, QUOTENAME(loginname) AS LoginName FROM sys.syslogins WHERE isntname = 1;"; IDataReader dr = null; try { dr = cmd.ExecuteReader(CommandBehavior.SingleResult); while (dr.Read()) { if (!_PrincipalExistsBySid((byte[])dr["sid"], domainContext)) results.Add((string)dr["LoginName"]); } } finally { if ((dr != null) && !dr.IsClosed) dr.Close(); } return results; } private static bool _PrincipalExistsBySid(byte[] principalSid, ContextType domainContext) { SecurityIdentifier sid = new SecurityIdentifier(principalSid, 0); if (sid.IsWellKnown) return true; using (PrincipalContext pc = new PrincipalContext(domainContext)) { return AuthenticablePrincipal.FindByIdentity(pc, IdentityType.Sid, sid.Value) != null; } } } }