All scripts
Microsoft 365 812

Bulk Set Mailbox Auto-Reply Status

Turns Outlook automatic replies on, off, or scheduled for a batch of mailboxes at once, reading the mailbox list and messages from a CSV file instead of clicking through each mailbox one by one.

Set-BulkMailboxAutoReply.ps1
<#
.SYNOPSIS
    Sets Outlook automatic reply (out of office) status for a batch of mailboxes from a CSV file.

.DESCRIPTION
    Reads a CSV of mailboxes and turns automatic replies on or off for each one in Exchange
    Online, with separate internal and external messages and optional start/end times for a
    scheduled reply. Useful for things like setting an out of office message for a whole team
    before a public holiday, or clearing replies for everyone the morning after.

    The CSV needs these columns:
      Mailbox        - the email address or alias of the mailbox
      State           - Enabled, Disabled, or Scheduled
      InternalMessage - message shown to people inside the organization
      ExternalMessage - message shown to people outside the organization
      StartTime       - only needed when State is Scheduled, format yyyy-MM-dd HH:mm
      EndTime         - only needed when State is Scheduled, format yyyy-MM-dd HH:mm

.AUTHOR
    Shehryar Hassan

.EXAMPLE
    .\Set-BulkMailboxAutoReply.ps1 -CsvPath .\autoreply-list.csv

    Reads autoreply-list.csv and applies the auto-reply settings to every mailbox listed in it.

.EXAMPLE
    .\Set-BulkMailboxAutoReply.ps1 -CsvPath .\autoreply-list.csv -WhatIf

    Shows what would change for each mailbox without actually applying anything.
#>

[CmdletBinding(SupportsShouldProcess = $true)]
param(
    [Parameter(Mandatory = $true)]
    [string]$CsvPath
)

if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
    Write-Error "The ExchangeOnlineManagement module is not installed. Run: Install-Module ExchangeOnlineManagement"
    return
}

if (-not (Get-ConnectionInformation)) {
    Write-Host "Connecting to Exchange Online..."
    Connect-ExchangeOnline -ShowBanner:$false
}

if (-not (Test-Path $CsvPath)) {
    Write-Error "CSV file not found at path: $CsvPath"
    return
}

$rows = Import-Csv -Path $CsvPath

if ($rows.Count -eq 0) {
    Write-Warning "No rows found in $CsvPath, nothing to do."
    return
}

$results = @()

foreach ($row in $rows) {
    $mailbox = $row.Mailbox
    $state = $row.State

    if ([string]::IsNullOrWhiteSpace($mailbox)) {
        Write-Warning "Skipping a row with no mailbox value."
        continue
    }

    if ($state -notin @("Enabled", "Disabled", "Scheduled")) {
        Write-Warning "Skipping $mailbox, State must be Enabled, Disabled, or Scheduled, got '$state'."
        continue
    }

    $params = @{
        Identity        = $mailbox
        AutoReplyState  = $state
        InternalMessage = $row.InternalMessage
        ExternalMessage = $row.ExternalMessage
    }

    if ($state -eq "Scheduled") {
        if ([string]::IsNullOrWhiteSpace($row.StartTime) -or [string]::IsNullOrWhiteSpace($row.EndTime)) {
            Write-Warning "Skipping $mailbox, State is Scheduled but StartTime or EndTime is missing."
            continue
        }
        $params.StartTime = [datetime]$row.StartTime
        $params.EndTime = [datetime]$row.EndTime
    }

    if ($PSCmdlet.ShouldProcess($mailbox, "Set auto-reply state to $state")) {
        try {
            Set-MailboxAutoReplyConfiguration @params -ErrorAction Stop
            $results += [pscustomobject]@{
                Mailbox = $mailbox
                State   = $state
                Status  = "Updated"
            }
        }
        catch {
            $results += [pscustomobject]@{
                Mailbox = $mailbox
                State   = $state
                Status  = "Failed: $($_.Exception.Message)"
            }
        }
    }
}

$results | Format-Table -AutoSize

$failed = $results | Where-Object { $_.Status -like "Failed*" }
if ($failed) {
    Write-Warning "$($failed.Count) mailbox(es) failed, check the Status column above for details."
}

Read it before you run it, and test in a safe tenant first.