Skip to content

Instantly share code, notes, and snippets.

@win2000b
Created February 20, 2026 11:31
Show Gist options
  • Select an option

  • Save win2000b/207d18f1a2a13b0aa844a11e1d117f1e to your computer and use it in GitHub Desktop.

Select an option

Save win2000b/207d18f1a2a13b0aa844a11e1d117f1e to your computer and use it in GitHub Desktop.
Goes through CSV and unhides users in Exchange Online
<# ------------------------------------------------------------
UnhideFromGAL_FromExport_WithStatusCsv.ps1
Uses the export CSV from the proxy export (Identity,Type,Value).
Processes UNIQUE identities and sets:
HiddenFromAddressListsEnabled = $false
Writes a status CSV in the same format as the import CSV log:
Identity,Type,Value,Status,Message,Timestamp
Hard-coded paths.
Supports -WhatIf / -Confirm via ShouldProcess.
Notes:
- This will only "unhide" (set to false). It will not hide anyone.
- Requires Get-Mailbox / Set-Mailbox rights.
------------------------------------------------------------- #>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
param()
# ----------------------------
# Hard-coded settings
# ----------------------------
$InputCsv = "C:\Data\UnhideGAL.csv"
$StatusCsv = "C:\Data\UnhideGAL_Status_{0}.csv" -f (Get-Date -Format "yyyyMMdd_HHmmss")
# If your CSV is semicolon-delimited, set this to ';' otherwise ','
$Delimiter = ','
# ----------------------------
# Helpers
# ----------------------------
function New-StatusRow {
param(
[string]$Identity,
[string]$Type,
[string]$Value,
[string]$Status,
[string]$Message
)
[pscustomobject]@{
Identity = $Identity
Type = $Type
Value = $Value
Status = $Status
Message = $Message
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
}
}
Write-Host "Reading identities from: $InputCsv" -ForegroundColor Cyan
Write-Host "Writing status CSV to: $StatusCsv" -ForegroundColor Cyan
if (-not (Test-Path $InputCsv)) {
throw "Input CSV not found: $InputCsv"
}
# Import export file
$rows = Import-Csv -Path $InputCsv -Delimiter $Delimiter
if (-not $rows -or $rows.Count -eq 0) {
throw "No rows found in CSV: $InputCsv"
}
# Validate required column exists
$cols = $rows[0].PSObject.Properties.Name
if ($cols -notcontains "Identity") {
throw "CSV must contain column 'Identity'. Found: $($cols -join ', ')"
}
# Build a unique list of identities, while keeping a representative Type/Value for logging format
$identityMap = @{} # Identity -> @{Type=; Value=}
foreach ($r in $rows) {
$id = ([string]$r.Identity).Trim()
if ([string]::IsNullOrWhiteSpace($id)) { continue }
if (-not $identityMap.ContainsKey($id)) {
# Keep first seen Type/Value (if present) just for log shape
$t = ""
$v = ""
if ($cols -contains "Type") { $t = ([string]$r.Type).Trim() }
if ($cols -contains "Value") { $v = [string]$r.Value }
$identityMap[$id] = @{ Type = $t; Value = $v }
}
}
$statusOut = New-Object System.Collections.Generic.List[object]
if ($identityMap.Keys.Count -eq 0) {
$statusOut.Add((New-StatusRow -Identity "" -Type "" -Value "" -Status "Error" -Message "No valid Identity values found in CSV"))
$statusOut | Export-Csv -Path $StatusCsv -NoTypeInformation -Encoding UTF8
throw "No valid Identity values found in CSV."
}
foreach ($id in ($identityMap.Keys | Sort-Object)) {
$t = $identityMap[$id].Type
$v = $identityMap[$id].Value
try {
# Get mailbox
$mbx = Get-Mailbox -Identity $id -ErrorAction Stop
# If already unhidden, record and skip
if ($mbx.HiddenFromAddressListsEnabled -eq $false) {
$statusOut.Add((New-StatusRow -Identity $id -Type $t -Value $v -Status "SkippedAlreadyUnhidden" -Message "HiddenFromAddressListsEnabled is already False"))
continue
}
if ($PSCmdlet.ShouldProcess($id, "Set HiddenFromAddressListsEnabled to False (Unhide from GAL)")) {
Set-Mailbox -Identity $id -HiddenFromAddressListsEnabled $false -ErrorAction Stop
$statusOut.Add((New-StatusRow -Identity $id -Type $t -Value $v -Status "Updated" -Message "Mailbox unhidden from GAL (HiddenFromAddressListsEnabled=False)"))
}
else {
$statusOut.Add((New-StatusRow -Identity $id -Type $t -Value $v -Status "WhatIf" -Message "Would unhide mailbox from GAL"))
}
}
catch {
$statusOut.Add((New-StatusRow -Identity $id -Type $t -Value $v -Status "Error" -Message $_.Exception.Message))
continue
}
}
$statusOut | Export-Csv -Path $StatusCsv -NoTypeInformation -Encoding UTF8
Write-Host "Done. Status written to: $StatusCsv" -ForegroundColor Green
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment