SQL Server-Skript zum Löschen von Konten nicht mehr in Active Directory

8

Wir haben einen SQL Server 2000, der in Kürze auf SQL Server 2005 migriert wird. Es wurden jahrelange Windows-Authentifizierungskonten erstellt, die in Active Directory nicht mehr vorhanden sind. Dadurch wird verhindert, dass der Assistent zum Kopieren von Datenbanken diese Konten auf dem neuen Server erstellt.

Gibt es ein Skript oder eine automatisierte Methode zum Löschen der Konten, die in unserem Active Directory nicht mehr vorhanden sind?


BEARBEITEN: Um klar zu sein, die Anmeldungen, die gelöscht werden müssen, befinden sich in SQL Server 2000, das den DROP LOGINBefehl nicht unterstützt .

Das manuelle Löschen der Anmeldungen in SQL Server 2000 würde (glaube ich) separat erfolgen, exec sp_droplogin 'loginname'aber bei mir kann der Anmeldename nicht gefunden werden, unabhängig davon, ob ich 'domain \ loginname' oder 'loginname' verwende.

Nur um die Verwirrung zu verstärken, exec sp_revokelogin 'domain\loginname'scheint zu funktionieren.

EDIT 2: Endlich das Problem behoben. Viele der problematischen Anmeldungen wurden programmgesteuert zur Datenbank hinzugefügt, und während sie in dem Sinne arbeiteten, dass ein Benutzer eine Verbindung herstellen konnte, stimmte der Benutzername mit dem NT-Anmeldenamen nicht mit Anmeldungen mit Domänenpräfix überein, wenn SQL Server keine Domäne erwartete, und umgekehrt umgekehrt.

Um dies zu beheben, habe ich die Prozedur sp_droplogin geändert, um eine der fehlerhaften Prüfungen durchzuführen.

Ich akzeptiere meine eigene Antwort, da sie in SQL Server 2000 funktioniert.


quelle

Antworten:

6

Am Ende habe ich die Konten aufgelistet mit:

    exec sp_validatelogins

Und läuft

    exec sp_dropuser loginname
    exec sp_droplogin loginname

auf die Ergebnisse.


quelle
4

Meinem ursprünglichen Kommentar zufolge scheint die SUSER_SIDFunktion nur die Seite zu erfassen, die beim Erstellen der Anmeldung aufgezeichnet wurde, und Active Directory nicht abzufragen (sinnvoll, da dies teuer sein kann - ich habe sogar versucht, den Serverdienst neu zu starten).

Hier ist eine C # -Konsolenanwendung, die die Aufgabe ausführt und es Ihnen ermöglicht, die Anmeldungen zu überwachen, die gelöscht werden, bevor sie tatsächlich gelöscht werden.

Für diese App ist .NET 3.5 oder höher erforderlich, und theoretisch kann sie in ein PowerShell-Skript eingefügt werden (ich bin mit der direkten Programmierung viel besser vertraut).

Um alle Anmeldungen lokaler / Computer-Benutzerkonten vom Server zu entfernen, müssen Sie diese Anwendung auf dem Server ausführen und die ContextTypeVariable fest codieren (ich habe sie so zum Testen auf meinem Heimcomputer, der keiner Domäne angehört ). Andernfalls können Sie es von jedem Computer in derselben Domäne wie der Server ausführen, der auch Zugriff auf den Server hat.

Ich werde dies in meinem Blog veröffentlichen, nachdem ich die Parameter externalisiert und den Code ein wenig bereinigt habe. Wenn ich das mache, werde ich diesen Beitrag bearbeiten. Aber damit können Sie jetzt loslegen.

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;
            }
        }
    }
}
Jon Seigel
quelle
Ich war bei anderen Projekten unterwegs und hatte bis jetzt keine Gelegenheit, dies zu versuchen. Ich denke, SQL Server 2005 wird auf einem anderen Server als SQL Server 2000 installiert, und die Funktionen sys.syslog und DROP LOGIN werden von SQL Server 2000 nicht unterstützt - die Datenbank wird nicht auf SQL Server übertragen 2005 wegen fehlgeschlagener Anmeldeerstellung.
@ Emgee: Ohhh, ich habe den Ball total fallen lassen. Es tut uns leid. Ich hoffe, es ist klar, wo Sie den Befehl zum Löschen der Anmeldung für SQL Server 2000 einfügen können. Ich hatte keine Instanz zum Testen, als ich dies schrieb.
Jon Seigel
Keine Sorge, Änderungen waren einfach genug.
4

Sie können xp_logininfo für diesen Prozess nutzen. Diese erweiterte gespeicherte Prozedur kann verwendet werden, um Informationen aus Active Directory für Windows-Anmeldungen in SQL Server bereitzustellen. Die Prozedur gibt einen Fehler zurück, wenn keine Anmeldung vorhanden ist. Daher können wir einen TRY / CATCH-Block um sie setzen, um SQL für Anmeldungen bereitzustellen, die bei Prozedurfehlern nicht mehr gültig sind:

declare @user sysname
declare @domain varchar(100)

set @domain = 'foo'

declare recscan cursor for
select name from sys.server_principals
where type = 'U' and name like @domain+'%'

open recscan 
fetch next from recscan into @user

while @@fetch_status = 0
begin
    begin try
        exec xp_logininfo @user
    end try
    begin catch
        --Error on xproc because login doesn't exist
        print 'drop login '+convert(varchar,@user)
    end catch

    fetch next from recscan into @user
end

close recscan
deallocate recscan

Bei der Funktionsweise des Skripts müssen Sie die Variable @domain auf die Domäne festlegen, die Sie überprüfen. Die Cursorabfrage filtert nur nach Windows-Anmeldungen (nicht nach Gruppen) innerhalb dieser Domäne. Sie erhalten Abfrageergebnisse für alle gültigen Anmeldungen, aber Drop-Anweisungen werden mit den Nachrichten gedruckt. Ich habe mich für den Druckansatz entschieden, anstatt SQL tatsächlich auszuführen, damit Sie die Ergebnisse überprüfen und validieren können, bevor Sie die Anmeldungen tatsächlich löschen.

Beachten Sie, dass dieses Skript nur Ihre Drop-Login-Anweisungen erstellt. Benutzer müssen weiterhin aus den jeweiligen Datenbanken entfernt werden. Die entsprechende Logik kann bei Bedarf zu diesem Skript hinzugefügt werden. Dies muss auch in Ihrer SQL 2005-Umgebung ausgeführt werden, da diese Logik in SQL 2000 nicht unterstützt wird.

Mike Fal
quelle
1
In acht nehmen! Wenn das SQL Server-Dienstkonto ein lokales Konto ist, Xp_logininfowird für ein gültiges Domänenkonto der Fehler 0x5 zurückgegeben, was bedeutet, dass der Zugriff verweigert wurde. Dies führt dazu, dass jedes Domänenkonto gelöscht wird. Die sp_validateloginsgespeicherte Prozedur führt zu denselben Ergebnissen, unabhängig davon, ob es sich bei dem SQL Server-Dienstkonto um ein lokales Konto oder ein Domänenkonto handelt.
Gili
0

Sie können eine Transaktion wie folgt löschen und neu erstellen:

BEGIN TRAN
BEGIN TRY
DROP LOGIN [DOMAIN\testuser]
CREATE LOGIN [DOMAIN\testuser] FROM WINDOWS;
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_LINE();
END CATCH
ROLLBACK  

Wenn der folgende Fehler auftritt: Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.Ihr Windows-Login existiert nicht mehr. Es gibt jedoch eine Reihe von Gründen, warum das Löschen selbst fehlschlägt (z. B. durch die Anmeldung erteilte Berechtigungen). Sie müssen diese manuell nachverfolgen.

Sebastian Meine
quelle
TRY ... CATCHwurde in SQL 2005 eingeführt. stackoverflow.com/questions/5552530/sql-server-2000-try-catch
Jon Seigel
Das ist richtig. Ich habe diese Einschränkung nicht gesehen. (Ich glaube, ich habe in der zweiten Zeile angefangen zu lesen ...) Sie können diese Methode wahrscheinlich immer noch verwenden, indem Sie einfach @@ ERROR überprüfen, anstatt den try catch zu verwenden. Ich habe jedoch keine so alte SQL Server-Installation verfügbar, um dies zu testen.
Sebastian Meine