프로그래밍 방식으로 Azure AD 테넌트의 모든 사용자 삭제

"프로그래밍 방식으로 Azure AD 테넌트 삭제"의 첫 번째 부분은 여기에 있습니다. 오늘은 모든 Azure AD 사용자(관리자 계정 제외)를 삭제할 것입니다. JP는 이를 달성하는 데 필요한 단계와 함께 전체 프로세스를 스트리밍했지만 언제든지 아래 코드나 블로그 게시물로 건너뛸 수 있습니다.



전제 조건


  • VS 코드
  • .NET Core 3.1(이상)
  • Azure AD B2C 테넌트

  • Azure AD 앱 등록



    Azure AD와 상호 작용하려면 앱 등록을 만들어야 합니다. Azure AD에서 앱 등록 -> 새 등록으로 이동합니다. 의미 있는 이름을 지정하고 지원되는 계정 유형에 대해 다중 테넌트를 선택합니다. 새 앱 등록에서 인증 및 플랫폼 추가로 이동합니다. 모바일 및 데스크톱을 선택하고 가장 적합한 URI(MSAL을 선호함)를 선택한 다음 공용 클라이언트 흐름 활성화 옵션을 선택해야 합니다. 이것을 저장하십시오! 마지막으로 필요한 MS Graph 권한을 추가하고 관리자 동의를 얻어야 합니다. API 권한에서 권한 추가 버튼을 누르고 위임된 권한을 선택한 다음 User.ReadWrite.All 권한을 검색합니다. 창 하단의 권한 추가를 꼭 눌러주세요.



    이것은 콘솔 앱이므로 인증하기 전에 관리자 동의를 제공해야 합니다. API 권한 블레이드에서 다음에 대한 관리자 동의 부여를 누릅니다.

    .

    이제 사용자 삭제 코드를 작성할 준비가 되었습니다...

    코드를 작성해 봅시다



    우리의 코드는
  • 현재 사용자 인증(대화형 인증)
  • 디렉토리에서 모든 사용자 개체 검색
  • 관리자 계정을 제외해야 합니다(그렇지 않으면 액세스 권한을 잃음)
  • .
  • 삭제 작업을 일괄 처리하고 일괄 처리를 실행합니다.

  • 즐겨찾는 터미널을 열고 다음을 입력하십시오.dotnet new console -n tenant_deleter
    또한 Azure AD 및 MS Graph와 상호 작용할 때 더 쉽게 사용할 수 있도록 몇 가지 NuGet 패키지를 추가해야 합니다. 터미널에 다음을 입력합니다.

    dotnet add package Microsoft.Graph
    dotnet add package Microsoft.Identity.Client
    




    선호하는 코드 편집기(VS Code 사용)에서 프로젝트를 열고 새 파일MsalTokenProvider.cs을 추가합니다. 다음 코드를 추가합니다.

    using System;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Microsoft.Graph;
    using Microsoft.Identity.Client;
    
    namespace tenant_deleter
    {
        // todo: stop building new MsalTokenProviders for every project
        public class MsalTokenProvider : IAuthenticationProvider
        {
            public readonly IPublicClientApplication _client;
            private readonly string[] _scopes = {
                 "https://graph.microsoft.com/User.ReadWrite.All"
                };
    
            public MsalTokenProvider(string tenantId)
            {
                _client = PublicClientApplicationBuilder
                   // mt app reg in separate tenant
                   // todo: move to config
                   .Create("<Your Azure AD App registration Client ID>")
                   .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
                   .Build();
            }
    
            public async Task AuthenticateRequestAsync(HttpRequestMessage request)
            {
                AuthenticationResult token;
                try
                {
                    // get an account ------ ??????
                    var account = await _client.GetAccountsAsync();
                    token = await _client.AcquireTokenSilent(_scopes, account.FirstOrDefault())
                        .ExecuteAsync();
                }
                catch (MsalUiRequiredException)
                {
                    token = await _client.AcquireTokenWithDeviceCode(
                        _scopes,
                        resultCallback =>
                        {
                            Console.WriteLine(resultCallback.Message);
                            return Task.CompletedTask;
                        }
    
                    ).ExecuteAsync();
                }
                request.Headers.Authorization =
                        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token.AccessToken);
            }
        }
    }
    


    이 코드는 Azure AD에 로그인하고 MS Graph용 액세스 토큰을 획득하는 역할을 합니다.

    필요한 코드를 추가하려면 Program.cs로 돌아가십시오.

    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    using Microsoft.Graph;
    
    namespace tenant_deleter
    {
        class Program
        {
            async static Task Main(string[] args)
            {
                string tenantId;
                if (args.Length < 1)
                {
                    Console.WriteLine("gimme a tenant");
                    tenantId = Console.ReadLine();
                }
                else
                {
                    tenantId = args[0];
                }
    
                var graph = new GraphServiceClient(new MsalTokenProvider(tenantId));
                var td = new ThingDeleter(graph);
                await td.DeleteAllUsersFromTenant();
                Console.WriteLine("*fin*");
                Console.ReadLine();
            }
        }
    
        public class ThingDeleter
        {
            private readonly GraphServiceClient _graphServiceClient;
            public ThingDeleter(GraphServiceClient client)
            {
                _graphServiceClient = client;
            }
    
            // delete all users from tenant
            public async Task DeleteAllUsersFromTenant()
            {
                var me = await _graphServiceClient.Me.Request().Select(x => x.Id).GetAsync();
                var users = await _graphServiceClient.Users.Request()
                    .Select(x => x.Id)
                    .Top(20)
                .GetAsync();
    
                var batch = new BatchRequestContent();
                var currentBatchStep = 1;
                var pageIterator = PageIterator<User>
                .CreatePageIterator(
                    _graphServiceClient,
                    users,
                    (user) =>
                    {
                        if (user.Id == me.Id) return true; //don't delete me
                        var requestUrl = _graphServiceClient
                                .Users[user.Id]
                                .Request().RequestUrl;
    
                        var request = new HttpRequestMessage(HttpMethod.Delete, requestUrl);
                        var requestStep = new BatchRequestStep(currentBatchStep.ToString(), request, null);
                        batch.AddBatchRequestStep(requestStep);
    
                        if (currentBatchStep == users.Count)
                        {
                            _graphServiceClient.Batch.Request().PostAsync(batch).GetAwaiter().GetResult();
                            currentBatchStep = 1; // batches are 1-indexed, weird
                            return true;
                        }
                        currentBatchStep++;
                        return true;
                    },
                    (req) =>
                    {
                        return req;
                    }
                );
                await pageIterator.IterateAsync();
            }
        }
    }
    


    Graph 코드는 일괄 처리를 사용하여 MS Graph에 대한 호출을 최적화합니다. 이는 특히 사용자가 1000명 이상인 대규모 테넌트의 경우에 필요합니다. 좋은 점은 이것을 병렬화하여 더욱 효율적으로 만들 수 있다는 것입니다 :)

    이제 dotnet build로 코드를 빌드하여 모든 것이 빌드되고 누락된 것이 없는지 확인할 수 있습니다.

    마지막으로 dotnet run <your Tenant Id>로 앱을 실행해 보겠습니다.



    작업이 완료되었습니다. 하나의 작업이 완료되고 몇 가지 작업이 더 남았습니다! Azure AD 테넌트를 삭제하는 방법에 대한 게시물을 확인하세요.

    코드를 보여주세요



    앱을 복제하고 실행하려면 GitHub Repo으로 이동하십시오.

    좋은 웹페이지 즐겨찾기