web-dev-qa-db-ja.com

Active Directoryに存在しないアカウントを削除するSQL Serverスクリプト

SQL Server 2005に間もなく移行されるSQL Server 2000があります。ActiveDirectoryに存在しないWindows認証アカウントが何年も作成されているため、データベースのコピーWizardがこれらを作成できません。新しいサーバー上のアカウント。

Active Directoryに存在しないアカウントを削除するスクリプトまたは自動化された方法はありますか?


編集:明確にするために、削除する必要のあるログインはSQL Server 2000上にあり、DROP LOGINコマンドをサポートしていません。

それとは別に、SQL Server 2000でログインを手動で削除することは(おそらく)exec sp_droplogin 'loginname'で行われますが、私の場合、「domain\loginname」または「loginname」のどちらを使用しても、ログイン名が見つかりません

混乱を増すために、exec sp_revokelogin 'domain\loginname'は機能しているように見えます。

編集2:最後に問題を解決しました。問題のあるログオンの多くはプログラムによってデータベースに追加され、ユーザーが接続できるという意味で機能していましたが、SQL Serverがドメインを予期していない場合、ユーザー名とNTログイン名はドメインプレフィックスのログオンが一致しませんでした。その逆。

これを解決するために、エラーが発生したチェックの1つを削除するようにsp_droploginプロシージャを変更しました。

SQL Server 2000で動作するので、自分の回答を受け入れます。

8
user16766

私がやったことは、アカウントをリストすることです:

    exec sp_validatelogins

そして走る

    exec sp_dropuser loginname
    exec sp_droplogin loginname

結果について。

6
user16766

このプロセスには xp_logininfo を利用できます。この拡張ストアドプロシージャを使用して、SQL ServerのWindowsログインにActive Directoryから情報を提供できます。ログインが存在しない場合、プロシージャはエラーを返します。そのため、TRY/CATCHブロックをその周りに配置して、プロシージャエラー時に無効になったログインのSQLを提供できます。

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

スクリプトが機能する方法では、@ domain変数を、チェック対象のドメインに設定する必要があります。カーソルクエリは、そのドメイン内の(グループではなく)Windowsログインでのみフィルタリングします。すべての有効なログインのクエリ結果が表示されますが、dropステートメントはメッセージとともに出力されます。実際にSQLを実行する代わりに、印刷アプローチを採用したため、実際にログインを削除する前に結果を確認および検証できます。

このスクリプトは、ドロップログインステートメントのみを作成することに注意してください。それでも、ユーザーはそれぞれのデータベースから削除する必要があります。必要に応じて、適切なロジックをこのスクリプトに追加できます。また、このロジックはSQL 2000ではサポートされていないため、SQL 2005環境で実行する必要があります。

4
Mike Fal

私の元のコメントによると、それはSUSER_SID関数は、ログインの作成時に記録されたsidを取得するだけで、実際にはActive Directoryにクエリを実行しません(コストがかかる可能性があるため、理にかなっています-サーバーサービスの再起動も試みました)。

これは、タスクを実行するC#コンソールアプリケーションで、実際に削除される前に削除されるログインを監査できます。

このアプリを実行するには.NET 3.5以降が必要です。理論的には、PowerShellスクリプトに組み込むことができます(直接プログラミングの方がはるかに快適です)。

ローカル/マシンユーザーアカウントのログインをサーバーから削除するには、このアプリケーションを実行する必要がありますonサーバーマシン、および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;
            }
        }
    }
}
4
Jon Seigel

次のようなトランザクションでドロップして再作成できます。

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  

発生するエラーがこれである場合:Windows NT user or group 'DOMAIN\testuser' not found. Check the name again.その後、Windowsログインはもう存在しません。ただし、ドロップ自体が失敗する理由はたくさんあります(ログインによって付与されるアクセス許可など)。それらを手動でフォローアップする必要があります。

0
Sebastian Meine