web-dev-qa-db-fra.com

SCRIPT SQL Server pour supprimer des comptes ne plus dans Active Directory

Nous avons un SQL Server 2000 qui sera prochainement migré vers SQL Server 2005. Il propose des années de comptes d'authentification Windows créés, qui n'existent plus dans Active Directory, qui empêchent la base de données de copie Wizard de la création comptes sur le nouveau serveur.

Existe-t-il un script ou une manière automatisée de supprimer les comptes qui n'existent plus dans notre Active Directory?


EDIT: Juste pour être clair, les connexions à supprimer sont sur SQL Server 2000, ce qui ne prend pas en charge le DROP LOGIN commande.

Séparément, supprimer manuellement les connexions dans SQL Server 2000 (je pense) être faite avec exec sp_droplogin 'loginname' Mais sur le mien, le nom de connexion introuvable, que j'utilise "domaine\loginName" ou "loginName"

Juste pour ajouter à la confusion, exec sp_revokelogin 'domain\loginname' semble fonctionner.

Edit 2: Enfin résolu le problème. De nombreux logons qui étaient problématiques ont été ajoutés de manière programmatique à la base de données et, bien qu'ils travaillaient en ce sens qu'un utilisateur puisse se connecter, le nom d'utilisateur VS NT Nom avait une inadéquation de logons préfixés de domaine lorsque SQL Server n'attendait aucun domaine et vice Versa.

Pour résoudre ce problème, j'ai modifié la procédure SP_DROPLOGIN pour supprimer l'un des chèques erreurs.

J'accepte ma propre réponse car cela fonctionne dans SQL Server 2000.

8
user16766

Ce que j'ai terminé de faire est de répertorier les comptes avec:

    exec sp_validatelogins

Et courir

    exec sp_dropuser loginname
    exec sp_droplogin loginname

sur les résultats.

6
user16766

Vous pouvez exploiter - xp_logininfo pour ce processus. Cette procédure stockée étendue peut être utilisée pour fournir des informations d'Active Directory pour Windows Logins dans SQL Server. La procédure renvoie une erreur si aucun identifiant n'existe, nous pouvons donc mettre un bloc d'essai/capteur autour de celui-ci pour fournir SQL pour connaître les connexions qui ne sont plus valides lorsque les erreurs de procédure:

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

Avec la façon dont le script fonctionne, vous devrez définir la variable @Domain sur n'importe quel domaine de votre vérification. La requête du curseur filtrera uniquement sur les connexions Windows (non des groupes) dans ce domaine. Vous obtiendrez des résultats de requête pour toutes les connexions valides, mais les instructions DROP seront imprimées avec les messages. Je suis allé avec l'approche d'impression au lieu d'exécuter le SQL afin que vous puissiez examiner et valider les résultats avant de laisser tomber les connexions.

Remarque, ce script ne créera que vos stations de connexion Drop. Les utilisateurs devront toujours être supprimés des bases de données respectives. La logique appropriée peut être ajoutée à ce script si nécessaire. De plus, cela devra être exécuté dans votre environnement SQL 2005, car cette logique n'est pas prise en charge dans SQL 2000.

4
Mike Fal

Par mon commentaire original, il apparaît le SUSER_SID Fonction ne fait que saisir tout ce que SID a été enregistré lorsque le login a été créé et ne doit pas réellement interroger Active Directory (a du sens, comme cela pourrait être coûteux - j'ai même essayé de redémarrer le service serveur).

Voici une application C # Console qui accomplit la tâche, vous permettant d'auditer les connexions qui seront abandonnées avant de ne pas être abandonnées.

Cette application nécessite .NET 3.5 ou plus à exécuter, et en théorie, il pourrait être mis dans un script PowerShell (je suis beaucoup plus à l'aise avec la programmation directe).

Pour supprimer toutes les connexions de comptes d'utilisateurs locaux/machine à partir du serveur, vous devez exécuter cette application On la machine de serveur et le code dur la variable ContextType (je l'ai comme celle-ci pour tester mon ordinateur à domicile non associé à des domaines). Sinon, vous pouvez l'exécuter à partir de n'importe quelle machine dans le même domaine que le serveur, qui a également accès au serveur.

Je vais poster cela sur mon blog après l'externalisation des paramètres et le nettoyage du code, alors quand je le fais, je modifierai ce post. Mais cela vous fera commencer maintenant.

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;
            }
        }
    }
}
4
Jon Seigel

Vous pouvez faire une goutte et recréer dans une transaction comme celle-ci:

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  

Si l'erreur que vous obtenez est la suivante: Windows NT user or group 'DOMAIN\testuser' not found. Check the name again. Ensuite, votre connexion Windows n'existe plus. Cependant, il y a un tas de raisons que la chute elle-même échouera (par exemple les autorisations accordées par la connexion). Vous devrez faire un suivi sur ceux manuellement.

0
Sebastian Meine