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.
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.
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.
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;
}
}
}
}
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.