Tập lệnh SQL Server để xóa tài khoản không còn trong Active Directory


8

Chúng tôi có SQL Server 2000 sẽ sớm được di chuyển sang SQL Server 2005. Nó có nhiều năm tài khoản Xác thực Windows được tạo không còn tồn tại trong Active Directory, điều này ngăn không cho Trình hướng dẫn cơ sở dữ liệu tạo các tài khoản này trên máy chủ mới.

Có tập lệnh hoặc một số cách tự động xóa các tài khoản không còn tồn tại trong Active Directory của chúng tôi không?


EDIT: Để rõ ràng, các thông tin đăng nhập cần phải xóa là trên SQL Server 2000, không hỗ trợ DROP LOGINlệnh.

Một cách riêng biệt, xóa thủ công đăng nhập trong SQL Server 2000 (tôi nghĩ) sẽ được thực hiện với exec sp_droplogin 'loginname'nhưng với tôi, tên đăng nhập không thể được tìm thấy, cho dù tôi sử dụng 'domain \ loginname' hay 'loginname'

Chỉ cần thêm vào sự nhầm lẫn, exec sp_revokelogin 'domain\loginname'có vẻ như để làm việc.

EDIT 2: Cuối cùng đã giải quyết vấn đề. Nhiều thông tin đăng nhập có vấn đề đã được lập trình thêm vào cơ sở dữ liệu và trong khi chúng hoạt động theo nghĩa người dùng có thể kết nối, tên người dùng và tên đăng nhập NT không khớp với các tên đăng nhập tiền tố tên miền khi SQL Server không có tên miền và ngược lại ngược lại

Để giải quyết vấn đề này, tôi đã sửa đổi thủ tục sp_droplogin để loại bỏ một trong những kiểm tra bị lỗi.

Tôi chấp nhận câu trả lời của riêng mình vì nó hoạt động trong SQL Server 2000.

Câu trả lời:


6

Những gì tôi đã kết thúc là liệt kê các tài khoản với:

    exec sp_validatelogins

Và chạy

    exec sp_dropuser loginname
    exec sp_droplogin loginname

về kết quả.


4

Theo nhận xét ban đầu của tôi, có vẻ như SUSER_SIDchức năng chỉ lấy bất cứ thứ gì được ghi lại khi đăng nhập được tạo và không thực sự truy vấn Active Directory (có nghĩa là nó có thể tốn kém - tôi thậm chí đã thử khởi động lại dịch vụ máy chủ).

Dưới đây là một ứng dụng bảng điều khiển C # hoàn thành nhiệm vụ, cho phép bạn kiểm tra các thông tin đăng nhập sẽ bị hủy trước khi chúng thực sự bị hủy.

Ứng dụng này yêu cầu .NET 3.5 trở lên để chạy và về lý thuyết, nó có thể được đưa vào tập lệnh PowerShell (Tôi thoải mái hơn nhiều với lập trình trực tiếp).

Để xóa bất kỳ thông tin đăng nhập nào của tài khoản người dùng cục bộ / máy chủ khỏi máy chủ, bạn sẽ cần chạy ứng dụng này trên máy chủ và mã hóa ContextTypebiến số (tôi có nó như thế để kiểm tra trên máy tính gia đình không tham gia miền của tôi ). Mặt khác, bạn có thể chạy nó từ bất kỳ máy nào trong cùng miền với máy chủ, cũng có quyền truy cập vào máy chủ.

Tôi sẽ đăng bài này lên blog của mình sau khi đưa ra các tham số và làm sạch mã một chút, vì vậy khi tôi làm điều đó, tôi sẽ chỉnh sửa bài đăng này. Nhưng điều này sẽ giúp bạn bắt đầu ngay bây giờ.

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;
            }
        }
    }
}

Tôi đã tắt các dự án khác và không có cơ hội để thử điều này cho đến bây giờ. Tôi nghĩ những gì tôi đang chạy là SQL Server 2005 được cài đặt trên một máy chủ khác với SQL Server 2000 và các hàm sys.syslog và DROP LOGIN không được SQL Server 2000 hỗ trợ - cơ sở dữ liệu sẽ không được chuyển sang SQL Server 2005 vì thất bại trong việc tạo đăng nhập.

@emgee: Ohhh tôi hoàn toàn đánh rơi quả bóng đó. Lấy làm tiếc. Tôi hy vọng rõ ràng nơi bạn có thể chèn lệnh để bỏ đăng nhập cho SQL Server 2000. Tôi không có phiên bản để kiểm tra khi tôi viết bài này.
Jon Seigel

Không phải lo lắng, sửa đổi là đủ dễ dàng.

4

Bạn có thể tận dụng xp_logininfo cho quá trình này. Quy trình lưu trữ mở rộng này có thể được sử dụng để cung cấp thông tin từ đăng nhập Active Directory cho Windows trong SQL Server. Quy trình trả về lỗi nếu không có thông tin đăng nhập, vì vậy chúng tôi có thể đặt khối TRY / CATCH xung quanh nó để cung cấp SQL cho các thông tin đăng nhập không còn hợp lệ khi lỗi thủ tục:

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

Với cách thức hoạt động của tập lệnh, bạn sẽ cần đặt biến @domain thành bất kỳ tên miền nào mà bạn kiểm tra. Truy vấn con trỏ sẽ chỉ lọc trên thông tin đăng nhập Windows (không phải nhóm) trong miền đó. Bạn sẽ nhận được kết quả truy vấn cho tất cả các thông tin đăng nhập hợp lệ, nhưng các câu lệnh thả sẽ được in cùng với các thông báo. Tôi đã thực hiện với phương pháp in thay vì thực sự thực thi SQL để bạn có thể xem lại và xác thực kết quả trước khi thực sự bỏ đăng nhập.

Lưu ý, tập lệnh này sẽ chỉ tạo ra số liệu đăng nhập thả của bạn. Người dùng vẫn sẽ cần phải được xóa khỏi cơ sở dữ liệu tương ứng. Logic thích hợp có thể được thêm vào kịch bản này khi cần thiết. Ngoài ra, điều này sẽ cần được chạy trong môi trường SQL 2005 của bạn, vì logic này không được hỗ trợ trong SQL 2000.


1
Coi chừng! Nếu tài khoản dịch vụ SQL Server là tài khoản cục bộ, Xp_logininfosẽ trả về lỗi 0x5, có nghĩa là quyền truy cập bị từ chối, đối với tài khoản miền hợp lệ. Điều này dẫn đến mọi tài khoản miền được liệt kê để thả. sp_validateloginsQuy trình được lưu trữ sẽ tạo ra kết quả tương tự cho dù tài khoản dịch vụ SQL Server là tài khoản cục bộ hay tài khoản miền.
Gili

0

Bạn có thể thực hiện thả và tạo lại trong một giao dịch như thế này:

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  

Nếu lỗi bạn gặp là đây: Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.thì đăng nhập windows của bạn không còn tồn tại nữa. Tuy nhiên, có rất nhiều lý do khiến bản thân việc thả sẽ thất bại (ví dụ: quyền được cấp bởi đăng nhập). Bạn sẽ cần phải theo dõi trên những người bằng tay.


TRY ... CATCHđã được giới thiệu trong SQL 2005. stackoverflow.com/questions/5552530/sql-server-2000-try-catch
Jon Seigel

Đúng rồi. Tôi đã không thấy hạn chế đó. (Tôi đoán rằng tôi đã bắt đầu đọc ở dòng thứ hai ...) Bạn có thể vẫn có thể sử dụng phương pháp này, chỉ cần kiểm tra @@ ERROR thay vì sử dụng tính năng bắt thử. Tuy nhiên, tôi không có bản cài đặt SQL Server cũ để kiểm tra cái này.
Sebastian Meine
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.