Per komentar asli saya, tampaknya SUSER_SID
fungsi hanya mengambil sid apa pun yang dicatat ketika login dibuat, dan tidak benar-benar meminta Active Directory (masuk akal, karena itu bisa mahal - saya bahkan mencoba me-restart layanan server).
Berikut adalah aplikasi konsol C # yang menyelesaikan tugas, memungkinkan Anda untuk mengaudit login yang akan dijatuhkan sebelum mereka benar-benar dijatuhkan.
Aplikasi ini membutuhkan .NET 3.5 atau lebih tinggi untuk dijalankan, dan secara teori itu bisa dimasukkan ke dalam skrip PowerShell (saya jauh lebih nyaman dengan pemrograman langsung).
Untuk menghapus login dari akun pengguna mesin / lokal dari server, Anda harus menjalankan aplikasi ini di mesin server, dan meng-hard-code ContextType
variabelnya (saya memilikinya seperti itu untuk pengujian pada komputer rumahan yang tidak terhubung dengan domain saya ). Jika tidak, Anda dapat menjalankannya dari mesin apa pun dalam domain yang sama dengan server, yang juga memiliki akses ke server.
Saya akan memposting ini di blog saya setelah eksternalisasi parameter dan membersihkan kode sedikit, jadi ketika saya melakukan itu, saya akan mengedit posting ini. Tapi ini akan membantu Anda memulai sekarang.
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;
}
}
}
}
Anda dapat memanfaatkan xp_logininfo untuk proses ini. Prosedur tersimpan yang diperluas ini dapat digunakan untuk memberikan informasi dari Active Directory untuk login Windows di SQL Server. Prosedur mengembalikan kesalahan jika tidak ada login, sehingga kami dapat menempatkan blok TRY / CATCH di sekitarnya untuk menyediakan SQL untuk login yang tidak lagi valid ketika prosedur kesalahan:
Dengan cara skrip bekerja, Anda harus mengatur variabel @domain ke domain apa pun yang Anda periksa. Kueri kursor akan memfilter hanya pada login Windows (bukan grup) dalam domain itu. Anda akan mendapatkan hasil permintaan untuk semua login yang valid, tetapi pernyataan drop akan dicetak dengan pesan. Saya menggunakan pendekatan cetak daripada benar-benar mengeksekusi SQL sehingga Anda dapat meninjau dan memvalidasi hasil sebelum benar-benar menjatuhkan login.
Catatan, skrip ini hanya akan membuat stat masuk drop Anda. Pengguna masih perlu dihapus dari database masing-masing. Logika yang sesuai dapat ditambahkan ke skrip ini sebagaimana diperlukan. Juga, ini perlu dijalankan di lingkungan SQL 2005 Anda, karena logika ini tidak didukung dalam SQL 2000.
sumber
Xp_logininfo
akan mengembalikan kesalahan 0x5, yang berarti akses ditolak, untuk akun domain yang valid. Ini menghasilkan setiap akun domain yang tercantum untuk dihapus.sp_validatelogins
Prosedur yang disimpan akan menghasilkan hasil yang sama apakah akun layanan SQL Server adalah akun lokal atau akun domain.Anda dapat melakukan drop dan membuat ulang dalam transaksi seperti ini:
Jika kesalahan yang Anda dapatkan adalah ini:
Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.
maka login windows Anda tidak ada lagi. Namun, ada banyak alasan bahwa drop itu sendiri akan gagal (mis. Izin yang diberikan oleh login). Anda harus menindaklanjutinya secara manual.sumber
TRY ... CATCH
diperkenalkan di SQL 2005. stackoverflow.com/questions/5552530/sql-server-2000-try-catch