Share via

Script modification or correction

Glenn Maxwell 13,761 Reputation points
2026-05-17T22:30:24.3666667+00:00

Hi All,

I am trying to execute the below script for around 10,000 users. I have the user principal names (UPNs) stored in a CSV file. However, the script is taking a very long time to run and I am not getting any output.

Could anyone please help review/correct the script or suggest a better approach for bulk processing? I have Global Administrator access.

My CSV file format is as below:  
UPN 
******@contoso.com  
******@contoso.com

$InputCsv  = "C:\Temp\list.csv"
$OutputCsv = "C:\Temp\output.csv" 

$users = Import-Csv $InputCsv

# Collect results
$result = foreach ($user in $users) {
    try {
        $regional = Get-MailboxRegionalConfiguration -Identity $user.UPN -ErrorAction Stop
        $calendar = Get-MailboxCalendarConfiguration -Identity $user.UPN -ErrorAction Stop

        [PSCustomObject]@{
            UPN                  = $user.UPN
            TimeZone             = $regional.TimeZone
            WorkingHoursTimeZone = $calendar.WorkingHoursTimeZone
        }
    }
    catch {
        [PSCustomObject]@{
            UPN                  = $user.UPN
            TimeZone             = "ERROR"
            WorkingHoursTimeZone = "ERROR"
        }
    }
}

# Export to CSV
$result | Export-Csv $OutputCsv -NoTypeInformation -Encoding UTF8 
Write-Host "Report exported to $OutputCsv"

Exchange Online
Exchange Online

A cloud-based service included in Microsoft 365, delivering scalable messaging and collaboration features with simplified management and automatic updates.

0 comments No comments

Answer accepted by question author

Teddie-D 16,370 Reputation points Microsoft External Staff Moderator
2026-05-18T00:30:43.4933333+00:00

Hi @Glenn Maxwell

I tested the script successfully with a sample CSV file and confirmed that the logic is working correctly.

The original issue was mainly due to the script processing a large number of Exchange Online remote cmdlet calls (two calls per mailbox), which can take significant time for thousands of users and may appear to hang without visible progress output.

I also encountered a PowerShell parsing issue related to the pipeline/export section, which was resolved by slightly restructuring the script to store results in an array before exporting.

Additionally, the warning message WARNING: Events from Email parameters of this cmdlet are deprecated. Use Get-EventsFromEmailConfiguration instead. appears to be generated by the Exchange Online module itself and does not affect the script execution.

For bulk-processing scenarios, I also tested the script successfully in PowerShell 7 using ForEach-Object -Parallel with a controlled throttle limit.

I encountered an issue where the UPN column became blank during parallel execution, which was resolved by assigning the UPN value to a local variable inside the parallel block before running the Exchange cmdlets.

The below version worked successfully during testing:

$InputCsv  = "C:\Temp\list.csv"
$OutputCsv = "C:\Temp\output.csv"
$users = Import-Csv $InputCsv
$results = $users | ForEach-Object -Parallel {
    $UPN = $_.UPN
    try {
        $regional = Get-MailboxRegionalConfiguration `
            -Identity $UPN `
            -WarningAction SilentlyContinue `
            -ErrorAction Stop
        $calendar = Get-MailboxCalendarConfiguration `
            -Identity $UPN `
            -WarningAction SilentlyContinue `
            -ErrorAction Stop
        [PSCustomObject]@{
            UPN                  = $UPN
            TimeZone             = $regional.TimeZone
            WorkingHoursTimeZone = $calendar.WorkingHoursTimeZone
        }
    }
    catch {
        [PSCustomObject]@{
            UPN                  = $UPN
            TimeZone             = "ERROR"
            WorkingHoursTimeZone = "ERROR"
        }
    }
} -ThrottleLimit 5
$results | Export-Csv $OutputCsv -NoTypeInformation -Encoding UTF8

A low throttle limit (for example 5–10) is still recommended to help reduce Exchange Online throttling during large-scale processing.

I hope this helps.


If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".       

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.  

Was this answer helpful?

1 person found this answer helpful.

2 additional answers

Sort by: Most helpful
  1. Kavya 655 Reputation points
    2026-05-19T05:52:57.56+00:00

    The logic looks correct, and the script should work as expected. How many objects are there in the CSV? If it contains thousands of mailboxes, the script may take a considerable amount of time to complete.

    To understand whether the script is still processing or stuck somewhere in between, you can add a Write-Progress statement with the mailbox currently being processed. That will help you track which mailbox the script is working on at any given time.

    Also, I’d recommend writing the output to the CSV incrementally after each mailbox is processed, rather than storing everything in memory and exporting it only at the end. This way, even if the script stops unexpectedly or encounters an error, you will still retain the data that was already processed instead of ending up with an empty output file.

    Was this answer helpful?

    0 comments No comments

  2. AI answer

    2026-05-17T22:30:45.59+00:00

    For 10,000 users, the script is slow because each Get-MailboxRegionalConfiguration and Get-MailboxCalendarConfiguration call is a separate remote command. A more efficient pattern is:

    1. Import the CSV once.
    2. Use Get-Mailbox (or a similar bulk cmdlet) to prefetch data where possible.
    3. Avoid per-iteration overhead such as repeated error handling where not needed.
    4. Run the script in an Exchange Online PowerShell session that is stable and optimized for long-running operations. For very large operations, using a helper module like RobustCloudCommand can improve reliability.

    Below is a more efficient and robust version of the script that keeps the same output format but reduces overhead and is better suited for bulk runs:

    $InputCsv  = 'C:\Temp\list.csv'
    $OutputCsv = 'C:\Temp\output.csv'
    
    # Import UPN list
    $users = Import-Csv -Path $InputCsv
    
    # Optional: build a hash set of UPNs for quick lookup if needed later
    $upnSet = @{}
    foreach ($u in $users) {
        if ($u.UPN) { $upnSet[$u.UPN.ToLower()] = $true }
    }
    
    # Collect results
    $results = foreach ($user in $users) {
        $upn = $user.UPN
    
        if (-not $upn) {
            [PSCustomObject]@{
                UPN                  = $null
                TimeZone             = 'ERROR'
                WorkingHoursTimeZone = 'ERROR'
            }
            continue
        }
    
        try {
            $regional = Get-MailboxRegionalConfiguration -Identity $upn -ErrorAction Stop
            $calendar = Get-MailboxCalendarConfiguration  -Identity $upn -ErrorAction Stop
    
            [PSCustomObject]@{
                UPN                  = $upn
                TimeZone             = $regional.TimeZone
                WorkingHoursTimeZone = $calendar.WorkingHoursTimeZone
            }
        }
        catch {
            [PSCustomObject]@{
                UPN                  = $upn
                TimeZone             = 'ERROR'
                WorkingHoursTimeZone = 'ERROR'
            }
        }
    }
    
    # Export once at the end
    $results | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
    Write-Host "Report exported to $OutputCsv"
    

    For very large runs that can take hours, using the RobustCloudCommand module to wrap the per-user calls can help handle transient throttling or connectivity issues in Exchange Online. The pattern is:

    1. Export or import the list of objects to a CSV.
    2. Import the CSV into a variable.
    3. Use Start-RobustCloudCommand with a script block that runs the per-user cmdlets.

    Example pattern (adapted from the documented usage):

    Install-Module -Name RobustCloudCommand -Force
    
    $UserList = Import-Csv 'C:\Temp\list.csv'
    
    Start-RobustCloudCommand `
        -LogFile 'C:\Temp\MailboxRegionalCalendar.log' `
        -Recipients $UserList `
        -ScriptBlock {
            param($input)
            $upn = $input.UPN
            $regional = Get-MailboxRegionalConfiguration -Identity $upn -ErrorAction Stop
            $calendar = Get-MailboxCalendarConfiguration  -Identity $upn -ErrorAction Stop
    
            [PSCustomObject]@{
                UPN                  = $upn
                TimeZone             = $regional.TimeZone
                WorkingHoursTimeZone = $calendar.WorkingHoursTimeZone
            }
        } `
        -IdentifyingProperty UPN `
        -UserPrincipalName '******@contoso.com'
    

    This approach is designed for long-running Exchange Online operations and can be more reliable for 10,000+ objects.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.