De acordo com o meu comentário original, parece que a SUSER_SID
função pega apenas o sid que foi gravado quando o logon foi criado e não consulta o Active Directory (faz sentido, pois isso pode ser caro - até tentei reiniciar o serviço do servidor).
Aqui está um aplicativo de console em C # que realiza a tarefa, permitindo auditar os logins que serão eliminados antes que eles sejam eliminados.
Este aplicativo requer a execução do .NET 3.5 ou superior e, em teoria, poderia ser colocado em um script do PowerShell (estou muito mais à vontade com a programação direta).
Para remover qualquer logon de contas de usuário local / de máquina do servidor, você precisará executar esse aplicativo na máquina do servidor e codificar a ContextType
variável (eu a tenho assim para testar no meu computador doméstico que não ingressou no domínio) ) Caso contrário, você poderá executá-lo a partir de qualquer máquina no mesmo domínio que o servidor, que também tenha acesso ao servidor.
Vou postar isso no meu blog depois de externalizar os parâmetros e limpar um pouco o código; portanto, quando fizer isso, editarei este post. Mas isso fará você começar agora.
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;
}
}
}
}
Você pode aproveitar o xp_logininfo para esse processo. Esse procedimento armazenado estendido pode ser usado para fornecer informações dos logons do Active Directory para Windows no SQL Server. O procedimento retornará um erro se não houver login, para que possamos colocar um bloco TRY / CATCH ao seu redor para fornecer SQL para logins que não são mais válidos quando o procedimento erros:
Com a maneira como o script funciona, você precisará definir a variável @domain para qualquer que seja o domínio que está verificando. A consulta do cursor filtrará apenas os logons do Windows (não grupos) nesse domínio. Você obterá resultados da consulta para todos os logins válidos, mas as instruções drop serão impressas com as mensagens. Eu fui com a abordagem de impressão em vez de realmente executar o SQL, para que você possa revisar e validar os resultados antes de interromper os logins.
Observe que esse script criará apenas suas estatísticas de login drop. Os usuários ainda precisarão ser removidos dos respectivos bancos de dados. A lógica apropriada pode ser adicionada a esse script, conforme necessário. Além disso, isso precisará ser executado no seu ambiente SQL 2005, pois essa lógica não é suportada no SQL 2000.
fonte
Xp_logininfo
retornará o erro 0x5, o que significa acesso negado, para uma conta de domínio válida. Isso resulta em todas as contas de domínio listadas para serem descartadas. Osp_validatelogins
procedimento armazenado produzirá os mesmos resultados, independentemente da conta de serviço do SQL Server ser uma conta local ou de domínio.Você pode soltar e recriar uma transação como esta:
Se o erro que você receber for este: o
Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.
seu login do Windows não existe mais. No entanto, existem vários motivos pelos quais a queda falhará (por exemplo, permissões concedidas pelo login). Você precisará acompanhar os manualmente.fonte
TRY ... CATCH
foi introduzido no SQL 2005. stackoverflow.com/questions/5552530/sql-server-2000-try-catch