Sql-Server

用於刪除 Active Directory 中不再存在的帳戶的 SQL Server 腳本

  • March 2, 2013

我們有一個很快將遷移到 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;
           }
       }
   }
}

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