• Как экспортировать OneDrive и .pst пользователя через Powershell?

    @Billy_Pluto Автор вопроса
    Роман Безруков , MaxKozlov спасибо вам за помощь, поигрался и нашел несколько ошибок, да и новенький гпт очень помог с подхватом SAS-токена. Проблема заключалась в том что он не подхватывал этот самый токен правильно, и забирал у предыдущего пользователя.
    Может кому-нибудь будет полезным:
    Param(
        [Parameter(Mandatory=$True, HelpMessage='Enter the email address that you want to export')]
        [string]$Mailbox,
        [Parameter(Mandatory=$True, HelpMessage='Enter the path where you want to save the PST file. !NO TRAILING BACKSLASH!')]
        [string]$ExportLocation
    )
    
    # Ensure the SharePoint Online Management Shell module is imported
    Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
    
    # Ensure the Exchange Online Management module is imported
    Import-Module ExchangeOnlineManagement
    
    # Function to get OneDrive URL for a user
    function Get-UserOneDriveLocation {
        param (
            [Parameter(Mandatory=$true)]
            [string]$UserPrincipalName
        )
        
        # Connect to SharePoint Online
        $SPOAdminURL = "https://tenant-admin.sharepoint.com"
        Connect-SPOService -Url $SPOAdminURL
        
        # Get the OneDrive URL
        $OneDriveLocation = Get-SPOSite -IncludePersonalSite $true -Limit ALL | Where-Object { $_.Owner -eq $UserPrincipalName } | Select-Object -ExpandProperty Url
        Disconnect-SPOService
        
        return $OneDriveLocation
    }
    
    # Create a search name. You can change this to suit your preference
    $SearchName = "$Mailbox PST"
    
    Write-Host "Connecting to Exchange Online. Enter your admin credentials in the pop-up window."
    Connect-ExchangeOnline
    
    # Get OneDrive URL for the user
    $OneDriveURL = Get-UserOneDriveLocation -UserPrincipalName $Mailbox
    if (-not $OneDriveURL) {
        Write-Host "Could not retrieve OneDrive URL for user $Mailbox"
        exit
    }
    
    Write-Host "OneDrive URL for user $Mailbox is $OneDriveURL"
    
    Write-Host "Creating compliance search..."
    New-ComplianceSearch -Name $SearchName -ExchangeLocation $Mailbox -SharePointLocation $OneDriveURL -AllowNotFoundExchangeLocationsEnabled $true -Confirm:$false
    
    # Create a content search, including the entire contents of the user's email and OneDrive.
    Write-Host "Starting compliance search..."
    Start-ComplianceSearch -Identity $SearchName -Confirm:$false
    
    Write-Host "Waiting for compliance search to complete..."
    do {
        Start-Sleep -Seconds 10
        $SearchStatus = (Get-ComplianceSearch -Identity $SearchName).Status
        Write-Host -NoNewline "."
    } while ($SearchStatus -ne "Completed")
    
    Write-Host "`nCompliance search is complete!"
    
    Write-Host "Creating export from the search..."
    $ExportAction = New-ComplianceSearchAction -SearchName $SearchName -Export -Format FxStream -ExchangeArchiveFormat PerUserPst -Scope BothIndexedAndUnindexedItems -EnableDedupe $true -SharePointArchiveFormat IndividualMessage -IncludeSharePointDocumentVersions $true -Confirm:$false
    
    Write-Host "Waiting for export to be ready..."
    do {
        Start-Sleep -Seconds 10
        $ExportStatus = (Get-ComplianceSearchAction -Identity $ExportAction.Identity).Status
        Write-Host -NoNewline "."
    } while ($ExportStatus -ne "Completed")
    
    Write-Host "`nExport is ready. Fetching the download URL..."
    
    # Locate the Unified Export Tool executable
    $ExportExe = ((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter microsoft.office.client.discovery.unifiedexporttool.exe -Recurse).FullName | Where-Object { $_ -notmatch "_none_" } | Select-Object -First 1)
    
    # Gather the URL and Token from the export in order to start the download
    $ExportName = $SearchName + "_Export"
    $ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details # Get details for the export action
    
    # Split the Container URL and Token from $ExportDetails
    $ExportDetails = $ExportDetails.Results.split(";")
    $ExportContainerUrl = $ExportDetails[0].trimStart("Container url: ")
    $ExportSasToken = $ExportDetails[1].trimStart(" SAS token: ")
    $ExportEstSize = ($ExportDetails[18].TrimStart(" Total estimated bytes: ") -as [double])
    $ExportTransferred = ($ExportDetails[20].TrimStart(" Total transferred bytes: ") -as [double])
    $ExportProgress = $ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")
    $ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")
    
    # Download the exported files from Office 365
    Write-Host "Initiating download"
    Write-Host "Saving export to: " + $ExportLocation
    $Arguments = "-name ""$SearchName""","-source ""$ExportContainerUrl""","-key ""$ExportSasToken""","-dest ""$ExportLocation""","-trace true"
    Start-Process -FilePath "$ExportExe" -ArgumentList $Arguments
    
    # Monitor the progress
    while (Get-Process microsoft.office.client.discovery.unifiedexporttool -ErrorAction SilentlyContinue) {
        $Downloaded = Get-ChildItem $ExportLocation\$SearchName -Recurse | Measure-Object -Property Length -Sum | Select-Object -ExpandProperty Sum
        Write-Progress -Id 1 -Activity "Export in Progress" -Status "Complete..." -PercentComplete $ExportProgress
        if ("Completed" -notlike $ExportStatus) {
            Write-Progress -Id 2 -Activity "Download in Progress" -Status "Estimated Complete..." -PercentComplete ($Downloaded / $ExportEstSize * 100) -CurrentOperation "$Downloaded/$ExportEstSize bytes downloaded."
        } else {
            Write-Progress -Id 2 -Activity "Download in Progress" -Status "Complete..." -PercentComplete ($Downloaded / $ExportEstSize * 100) -CurrentOperation "$Downloaded/$ExportTransferred bytes downloaded."
        }
        Start-Sleep 60
        $ExportDetails = Get-ComplianceSearchAction -Identity $ExportName -IncludeCredential -Details # Get details for the export action
        $ExportDetails = $ExportDetails.Results.split(";")
        $ExportEstSize = ($ExportDetails[18].TrimStart(" Total estimated bytes: ") -as [double])
        $ExportTransferred = ($ExportDetails[20].TrimStart(" Total transferred bytes: ") -as [double])
        $ExportProgress = $ExportDetails[22].TrimStart(" Progress: ").TrimEnd("%")
        $ExportStatus = $ExportDetails[25].TrimStart(" Export status: ")
        Write-Host -NoNewline " ."
    }
    
    Write-Host "Export and download process completed successfully."
    Написано
  • Как экспортировать OneDrive и .pst пользователя через Powershell?

    @Billy_Pluto Автор вопроса
    Alexey Dmitriev, Так и делал в тот же день, ошибки нашел, и исправлял их разными способами, и такой злой, и уставший стал, что сюда написал). В итоге решил, но возникли другие вопросы
    Написано
  • Как экспортировать OneDrive и .pst пользователя через Powershell?

    @Billy_Pluto Автор вопроса
    Роман Безруков, MaxKozlov
    Пользователь имеет все права и роли. Я тестировал на своей учетной записи, которая имеет статус Global Admin, и на всякий случай добавил себе права в EDiscovery. Ошибку соединения я решил, запустив PowerShell от имени администратора, а не из VSCode, как делал до этого.

    Что касается части с установкой, я уже убрал её из скрипта.

    Самое интересное, что скрипт почему-то не дожидается окончания сбора данных для "Экспорта" и начинает загрузку данных предыдущего пользователя, экспорт которого был сделан давно.
    Пример:
    Я запустил скрипт для пользователя с адресом "test@domain.com", и он успешно выполнил экспорт всех данных по указанной локации. При повторном запуске скрипта для адреса "test2@domain.com", скрипт перестал ожидать окончания процесса экспорта, возможно из-за большого объема данных пользователя. На сайте "compliance.microsoft.com" процесс остановился на этапе "Preparing Data". Затем скрипт прекратил загрузку данных для "test2@domain.com_PST" и начал скачивать "test@domain.com_PST", хотя в параметрах скрипта этот пользователь не упоминается.

    Интервал ожидания поднимал до "Start-Sleep -s 90", так же менял имя с добавлением даты и времени, но все тоже самое получалось
    Написано
  • Как экспортировать OneDrive и .pst пользователя через Powershell?

    @Billy_Pluto Автор вопроса
    Alexey Dmitriev, Добрый день,
    Я не в коем случае не это имел ввиду:)
    Вчера было поздно, и очень уставшим, вот и такого рода получилось сообщение извиняюсь. Я заметил что в логах, я не могу подключиться к Azure Storage, но не могу решить данный вопрос.
    Написано
  • Как экспортировать OneDrive и .pst пользователя через Powershell?

    @Billy_Pluto Автор вопроса
    А тут логи:
    [2024-05-10 22:15:03.7951][1][Information]: UnifiedExportTool engine version: 15.20.1771.000, CLR version: 4.0.30319.42000.
    [2024-05-10 22:15:03.7961][1][Information]: Current local time: 11.05.2024 03:15:03, current UTC time: 10.05.2024 22:15:03.
    [2024-05-10 22:15:03.7961][1][Information]: OS version: Microsoft Windows NT 6.2.9200.0, processor count: 20, x64: True, working set: 31125504.
    [2024-05-10 22:15:03.7961][1][Information]: Export name: a.bissengaliyeva@norsec.kz PST.
    [2024-05-10 22:15:03.7976][1][Information]: ConstantProvider..Ctor: Registry not found for constants
    [2024-05-10 22:15:03.8545][5][Information]: Start progress controller loop.
    [2024-05-10 22:15:03.9045][1][Information]: Effective downloader concurrency: 48
    [2024-05-10 22:15:03.9065][5][Information]: Start result recorder loop.
    [2024-05-10 22:15:04.7046][1][Error]: Fatal error happened. Microsoft.WindowsAzure.Storage.StorageException: Удаленный сервер возвратил ошибку: (404) Не найден. ---> System.Net.WebException: Удаленный сервер возвратил ошибку: (404) Не найден.
       в Microsoft.WindowsAzure.Storage.Shared.Protocol.HttpResponseParsers.ProcessExpectedStatusCodeNoException[T](HttpStatusCode expectedStatusCode, HttpStatusCode actualStatusCode, T retVal, StorageCommandBase`1 cmd, Exception ex)
       в Microsoft.WindowsAzure.Storage.Blob.CloudBlobSharedImpl.<>c__DisplayClassa.<FetchAttributesImpl>b__9(RESTCommand`1 cmd, HttpWebResponse resp, Exception ex, OperationContext ctx)
       в Microsoft.WindowsAzure.Storage.Core.Executor.Executor.EndGetResponse[T](IAsyncResult getResponseResult)
       --- Конец трассировки внутреннего стека исключений ---
       в Microsoft.WindowsAzure.Storage.Core.Util.StorageAsyncResult`1.End()
       в Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.EndOpenRead(IAsyncResult asyncResult)
       в Microsoft.WindowsAzure.Storage.Core.Util.AsyncExtensions.<>c__DisplayClass1`1.<CreateCallback>b__0(IAsyncResult ar)
    --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
       в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       в Microsoft.Office.Compliance.Core.DiscoveryStore.DiscoveryStoreReader.<GetMetadataAsync>d__15.MoveNext()
    --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
       в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       в Microsoft.Office.Client.Discovery.UnifiedExport.Utils.ServerKnowledge.ServerExportJobTracker.<GetStoreMetadataAsync>d__6.MoveNext()
    --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
       в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       в Microsoft.Office.Client.Discovery.UnifiedExport.Engine.Lite.LiteExportEngine.<ServerJobMonitorLoop>d__7.MoveNext()
    --- Конец трассировка стека из предыдущего расположения, где возникло исключение ---
       в System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       в System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       в Microsoft.Office.Client.Discovery.UnifiedExport.Engine.Lite.LiteExportEngine.<Execute>d__4.MoveNext()
    Request Information
    RequestID:3068c791-e01e-0032-6d27-a3b240000000
    RequestDate:Fri, 10 May 2024 22:15:04 GMT
    StatusMessage:The specified resource does not exist.
    
    [2024-05-10 22:15:04.7096][1][Information]: Start to cancel the export.
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    Что можете посоветовать использовать?
    И как примерно это будет выглядеть ?
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    Роман Безруков, Не знал про такой вариант приделывания GUI к Powershell, почему-то в голове подумал что вы про ISE, но потом отпало ) . Погуглю посмотрю возможно будет подходящим вариантом)
    Благодарю за ссылочку по Ansible:)
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    MaxKozlov, Ну Windows/AD/Powershell уж точно реальные)
    А вот запускать данные "скрипты" в Jenkins будут все таки не просто офисные работники, а именно служба поддержки ИТ нашего офиса, просто ребята там, не особо горят делать, что-то новое и что-то сложное, а от руководства была поставлена задача автоматизировать такие вещи как создание юзеров и.т.д, потому что ребята очень часто косячат с аттрибутами при создании пользователей, и сказали убрать данный человеческий фактор. Но запуск с powershell, я уверен сделает еще хуже.
    Скоуп Джоб который дали чтобы выполнял Jenikns или что-то другое:
    1. Создание/Удаление пользователей
    2.Добавление/Удаление в группы AD
    3.Вытаскивать отчеты кто состоит в каких группах в AD
    4. Вытаскивать отчет о том у кого есть доступ к группе "P Driver(Общая папка)"
    5.Выгрузка пользователей в .xml формате для сверки кто еще работает, а кто нет.
    С 1С ничего такого нету(Но думаю на пока что).
    И таких не особо по сути важных Джоб очень много, я и сам конечно 3,4,5 вариант ручками могу сделать, там уже написанные скрипты я себе сделал и записал их в текстовый файлик). Но хочется также делегировать на ребят.
    Да, и очень интересно обучиться таким инструментам как "Jenkins" и "Ansible"
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    Роман Безруков, Благодарю про статью Дженкинса, и информацию, о том что не такой уж и хороший вариант ). Думаю попробую тогда изучить Ansible, но запуски данных скриптов будут делать поддержка офиса, и думаю для них будет немного тяжеловато их запускать, ведь они привыкли больше к UI где все легко и просто, а ansbile вроде не имеет таких штук.

    P.S. Не могли бы вы также поделиться ссылкой на ansible изучение если не затруднит ? Вроде как в гугле полно, да и на канале "ADV-IT", видал, но может у вас есть и по лучше варианты)
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    Роман Безруков, Не думал о том что это будет так выглядеть, знал что Jenkins CI/CD инструмент, но просто работаю на нескольких работах, и в одной из них видел что ребята для создания пользователей, и ресету пароля используют дженкинс, и показалось что вроде не плохая идея.
    А Ansible разве можно будет иметь некий UI где можно будет менять параметры для заполнения ?
    Написано
  • Можно ли связать Jenkins с Active Directory?

    @Billy_Pluto Автор вопроса
    MaxKozlov, не думаю что будет критично, да и вроде как выполнят джобы он разве не будет от лица юзера под которым запускает скрипт?
    Написано
  • Как сделать Уведомление создание и добавления в группу пользователя в AD?

    @Billy_Pluto Автор вопроса
    Добрый день всем! Спасибо за вашу помощь, извиняюсь за долгие ответы был в отпуске)
    Переписал скрипт с помощью GPT, и вашим советам)
    Присылает все отлично, только вот не понимаю почему Scheduler не запускает ее по отслеживанию ивента(
    Хотя вроде указывал по совету Романа, указал 4720. Но приходится в ручную запускать, и тогда отрабатывает как надо

    # SMTP server parameters
    $smtpUsername = "test@domain"
    $smtpPassword = "Password#"
    $smtpServer = "domain.mail.protection.outlook.com"
    $smtpPort = 25
    $smtpFrom = "test@domain"
    $smtpTo = "user@domain"
    $smtpSubject = "New User Created in Active Directory"
    
    # Function to send email
    function Send-Mail($message) {
        try {
            # Convert password to a secure string
            $securePassword = ConvertTo-SecureString -String $smtpPassword -AsPlainText -Force
            
            # Create email credentials
            $smtpCredentials = New-Object System.Management.Automation.PSCredential -ArgumentList $smtpUsername, $securePassword
            
            # Send email using Exchange Online
            Send-MailMessage -From $smtpFrom -To $smtpTo -Subject $smtpSubject -Body $message -SmtpServer $smtpServer -Port $smtpPort -UseSsl -Credential $smtpCredentials
        } catch {
            Write-Host "Error sending message: $_"
        }
    }
    
    
    $lastCheckFile = "C:\scripts\lastCheck.txt"
    
    $lastCheck = if (Test-Path $lastCheckFile) { Get-Content $lastCheckFile } else { (Get-Date).AddDays(-1) }
    
    
    Import-Module ActiveDirectory
    $users = Get-ADUser -Filter * -Properties whenCreated, DisplayName, EmailAddress
    foreach ($user in $users) {
        if ($user.whenCreated -gt $lastCheck) {
            $message = "User $($user.SamAccountName) has been created in Active Directory." +
                       "`nDisplay Name: $($user.DisplayName)" +
                       "`nEmail: $($user.EmailAddress)" +
                       "`nCreation Date: $($user.whenCreated)" +
                       "`nDomain: $($user.DistinguishedName)"
            Write-Host "Sending notification for user: $($user.SamAccountName)"
            Send-Mail $message
        }
    }
    
    
    Set-Content -Path $lastCheckFile -Value (Get-Date)
    Написано
  • Как сделать Уведомление создание и добавления в группу пользователя в AD?

    @Billy_Pluto Автор вопроса
    Роман Безруков, Я создал таску в планировщике через event viewer. А скрипт вроде как должен указывать username, и в группу какую добавили пользователя.
    Не могли бы подсказать как тогда его лучше всего подвязать к этим событиям?

    Хорошо в расписание добавлю аргумент
    Спасибо вам:)
    Написано