Skrip SQL Server untuk menghapus akun tidak lagi di Active Directory

8

Kami memiliki SQL Server 2000 yang akan segera dimigrasikan ke SQL Server 2005. Telah bertahun-tahun akun Windows Authentication dibuat yang tidak lagi ada di Active Directory, yang mencegah Copy Database Wizard dari membuat akun ini di server baru.

Apakah ada skrip atau cara otomatis menghapus akun yang tidak ada lagi di Direktori Aktif kami?


EDIT: Hanya untuk menjadi jelas, login yang perlu dihapus ada di SQL Server 2000, yang tidak mendukung DROP LOGINperintah.

Secara terpisah, secara manual menghapus login di SQL Server 2000 akan (saya pikir) dilakukan dengan exec sp_droplogin 'loginname'tetapi pada saya, nama login tidak dapat ditemukan, apakah saya menggunakan 'domain \ loginname' atau 'loginname'

Hanya untuk menambah kebingungan, exec sp_revokelogin 'domain\loginname'tampaknya berfungsi.

EDIT 2: Akhirnya diselesaikan masalah. Banyak log masuk yang bermasalah secara program ditambahkan ke database, dan sementara mereka bekerja dalam arti bahwa pengguna dapat terhubung, nama login nama pengguna vs NT memiliki ketidakcocokan log domain-awalan ketika SQL Server tidak mengharapkan domain, dan sebaliknya sebaliknya.

Untuk mengatasi ini, saya memodifikasi prosedur sp_droplogin untuk mengambil salah satu pemeriksaan yang salah.

Saya menerima jawaban saya sendiri karena berfungsi di SQL Server 2000.


sumber

Jawaban:

6

Yang akhirnya saya lakukan adalah mendaftarkan akun dengan:

    exec sp_validatelogins

Dan berlari

    exec sp_dropuser loginname
    exec sp_droplogin loginname

pada hasil.


sumber
4

Per komentar asli saya, tampaknya SUSER_SIDfungsi 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 ContextTypevariabelnya (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;
            }
        }
    }
}
Jon Seigel
sumber
Saya tidak aktif di proyek lain dan tidak mendapatkan kesempatan untuk mencoba ini sampai sekarang. Saya pikir apa yang saya jalankan adalah SQL Server 2005 diinstal pada server yang berbeda dari SQL Server 2000, dan fungsi sys.syslog dan DROP LOGIN tidak didukung oleh SQL Server 2000 - database tidak akan ditransfer ke SQL Server 2005 karena kegagalan pembuatan logon.
@ Emgee: Ohhh aku benar-benar menjatuhkan bola pada yang itu. Maaf. Saya harap ini jelas di mana Anda dapat memasukkan perintah untuk menjatuhkan login untuk SQL Server 2000. Saya tidak punya contoh untuk menguji ketika saya menulis ini.
Jon Seigel
Jangan khawatir, modifikasi cukup mudah.
4

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:

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

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.

Mike Fal
sumber
1
Waspadalah! Jika akun layanan SQL Server adalah akun lokal, Xp_logininfoakan mengembalikan kesalahan 0x5, yang berarti akses ditolak, untuk akun domain yang valid. Ini menghasilkan setiap akun domain yang tercantum untuk dihapus. sp_validateloginsProsedur yang disimpan akan menghasilkan hasil yang sama apakah akun layanan SQL Server adalah akun lokal atau akun domain.
Gili
0

Anda dapat melakukan drop dan membuat ulang dalam transaksi seperti ini:

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  

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.

Sebastian Meine
sumber
TRY ... CATCHdiperkenalkan di SQL 2005. stackoverflow.com/questions/5552530/sql-server-2000-try-catch
Jon Seigel
Itu betul. Saya tidak melihat batasan itu. (Saya kira saya mulai membaca di baris kedua ...) Anda mungkin masih dapat menggunakan metode ini, cukup memeriksa @@ ERROR daripada menggunakan try catch. Namun, saya tidak memiliki instalasi SQL Server yang lama tersedia untuk menguji ini.
Sebastian Meine