Согласно моему первоначальному комментарию, похоже, что 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;
}
}
}
}
Вы можете использовать xp_logininfo для этого процесса. Эта расширенная хранимая процедура может использоваться для предоставления информации из Active Directory для имен входа Windows в SQL Server. Процедура возвращает ошибку, если имени входа нет, поэтому мы можем поместить вокруг него блок TRY / CATCH, чтобы обеспечить SQL для имен входа, которые больше не действительны при ошибках процедуры:
Чтобы скрипт работал, вам нужно установить переменную @domain в любой домен, который вы проверяете. Запрос курсора будет фильтроваться только по логинам Windows (не по группам) в этом домене. Вы получите результаты запроса для всех действительных имен входа, но операторы отбрасывания будут напечатаны с сообщениями. Я пошел с подходом печати вместо того, чтобы фактически выполнить SQL, таким образом, вы можете просмотреть и проверить результаты прежде, чем фактически сбросить логины.
Обратите внимание, этот скрипт будет создавать только ваши дроп-логины. Пользователи по-прежнему должны быть удалены из соответствующих баз данных. Соответствующая логика может быть добавлена в этот скрипт по мере необходимости. Кроме того, это необходимо будет выполнить в вашей среде SQL 2005, поскольку эта логика не поддерживается в SQL 2000.
источник
Xp_logininfo
будет возвращена ошибка 0x5, что означает, что доступ запрещен для действительной учетной записи домена. Это приводит к удалению каждой учетной записи домена.sp_validatelogins
Хранимая процедура выдаст одинаковые результаты, независимо от того, является ли учетная запись службы SQL Server локальной или доменной учетной записью.Вы можете сделать сброс и воссоздать в транзакции, как это:
Если вы получаете ошибку:
Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.
ваш логин Windows больше не существует. Однако существует множество причин, по которым само удаление не будет выполнено (например, разрешения, предоставленные для входа в систему). Вам нужно будет следить за этим вручную.источник
TRY ... CATCH
была введена в SQL 2005. stackoverflow.com/questions/5552530/sql-server-2000-try-catch