私の元のコメントによると、この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;
}
}
}
}