<#
.SYNOPSIS
Clear Home Folder (local path) for each AD user in an OU and export results to CSV.
.PARAMETER SearchBase
Distinguished Name (DN) of the OU to search.
.PARAMETER WhatIfMode
If supplied, no changes are actually made.
.PARAMETER CsvPath
Output path for the CSV file.
#>
param(
[string]$SearchBase = 'OU=.Users (Role Based),OU=Steven Whiting,DC=whiting-steven,DC=co,DC=uk',
[switch]$WhatIfMode,
[string]$CsvPath = ".\HomeFolder_Clear_Report_$(Get-Date -Format yyyyMMdd_HHmmss).csv"
)
# Import AD module
Import-Module ActiveDirectory -ErrorAction Stop
Write-Output "SearchBase: $SearchBase"
if ($WhatIfMode) { Write-Output "WHATIF MODE ENABLED — no changes will be made." }
# Retrieve users
$users = Get-ADUser -Filter * -SearchBase $SearchBase -SearchScope Subtree -Properties homeDirectory,homeDrive,DistinguishedName,SamAccountName
if (-not $users) {
Write-Output "No users found under the specified OU."
exit
}
# Results array
$results = @()
foreach ($u in $users) {
$result = [PSCustomObject]@{
SamAccountName = $u.SamAccountName
DistinguishedName = $u.DistinguishedName
PreviousHomeDirectory = $u.homeDirectory
PreviousHomeDrive = $u.homeDrive
Action = ""
}
if ($WhatIfMode) {
$result.Action = "Would Clear"
$results += $result
continue
}
try {
Set-ADUser -Identity $u.DistinguishedName -Clear homeDirectory,homeDrive -Confirm:$false -ErrorAction Stop
$result.Action = "Cleared"
} catch {
$result.Action = "Failed: $($_.Exception.Message)"
}
$results += $result
}
# Export CSV
$results | Export-Csv -NoTypeInformation -Path $CsvPath
Write-Output "CSV report created: $CsvPath"
Write-Output "Completed."
Category Archives: Powershell
SMTP/Alias
For AD SMTP and Alias’
<#
.SYNOPSIS
Export SMTP addresses (primary + secondary) from AD users.
.DESCRIPTION
Uses Get-ADUser to collect the 'proxyAddresses' and 'mail' attributes.
- Primary SMTP is the address with the "SMTP:" prefix (uppercase).
- Other SMTP addresses are those with the "smtp:" prefix (lowercase).
- If proxyAddresses is empty, the script will attempt to use the 'mail' attribute as primary.
.PARAMETER OutputCsv
File path for exported CSV. Default: .\AD-SMTP-Addresses.csv
.PARAMETER SearchBase
Optional AD container (distinguishedName) to restrict the search.
.PARAMETER IncludeDisabled
If specified, include disabled accounts as well. By default disabled accounts are excluded.
.EXAMPLE
.\Export-AD-SMTP.ps1 -OutputCsv C:\temp\smtp-addresses.csv
#>
param(
[string]$OutputCsv = ".\AD-SMTP-Addresses.csv",
[string]$SearchBase,
[switch]$IncludeDisabled
)
# Ensure ActiveDirectory module is available
if (-not (Get-Module -ListAvailable -Name ActiveDirectory)) {
Write-Error "The ActiveDirectory module is not installed or available. Install RSAT/Active Directory module and run again."
exit 1
}
Import-Module ActiveDirectory -ErrorAction Stop
# Build filter
$filter = if ($IncludeDisabled) { { } } else { { Enabled -eq $true } }
# Properties we need
$properties = @("proxyAddresses","mail","distinguishedName","samAccountName","displayName","objectClass")
try {
if ($SearchBase) {
$users = Get-ADUser -Filter * -SearchBase $SearchBase -Properties $properties
} else {
$users = Get-ADUser -Filter * -Properties $properties
}
}
catch {
Write-Error "Failed to query Active Directory: $_"
exit 1
}
$result = foreach ($u in $users) {
# Some objects may not have proxyAddresses (or not be users) - handle safely
$proxy = @()
if ($u.proxyAddresses) {
$proxy = $u.proxyAddresses
}
# Normalize and split SMTP addresses
$primary = $null
$others = @()
if ($proxy.Count -gt 0) {
# find exact uppercase SMTP: for primary
$primaryEntry = $proxy | Where-Object { $_ -like "SMTP:*" } | Select-Object -First 1
if ($primaryEntry) {
$primary = $primaryEntry -replace '^[sS][mM][tT][pP]:','' # remove prefix (case-insensitive)
} else {
# no uppercase SMTP found -> try any smtp: entry as fallback
$fallback = $proxy | Where-Object { $_ -match '^(smtp|SMTP):' } | Select-Object -First 1
if ($fallback) { $primary = $fallback -replace '^[sS][mM][tT][pP]:','' }
}
$others = $proxy |
Where-Object { $_ -match '^(smtp|SMTP):' } |
Where-Object { ($_ -replace '^[sS][mM][tT][pP]:','') -ne $primary } |
ForEach-Object { $_ -replace '^[sS][mM][tT][pP]:','' }
}
# If no proxyAddresses, fall back to mail attribute as primary (if present)
if (-not $primary -and $u.mail) {
$primary = $u.mail
}
# Build object for export
[PSCustomObject]@{
DistinguishedName = $u.DistinguishedName
SamAccountName = $u.SamAccountName
DisplayName = $u.DisplayName
ObjectClass = $u.ObjectClass
PrimarySMTP = $primary
OtherSMTPs = if ($others.Count -gt 0) { $others -join ";" } else { $null }
}
}
# Export to CSV
try {
$result | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
Write-Output "Export completed: $OutputCsv ('$($result.Count) records)'"
}
catch {
Write-Error "Failed to export CSV: $($_)"
exit 1
}
Powershell – Get SAM name
Gets the SAM name of users in a specific OU and then displays them as swhiting, jdoe, ldoe
etc, this was so I could then put them in another script.
(Get-ADUser -SearchBase "OU=FinanceTeams,OU=CustomerServices,OU=Operations,OU=.Users,OU=stevenwhiting,DC=stevenwhiting,DC=co,DC=uk" -Filter {Enabled -eq $true} | Select-Object -ExpandProperty SamAccountName | ForEach-Object { "'$_'" }) -join ','
Powershell – to change all users UNP from swhiting to steven.whiting
Wasn’t perfect but did what needed. Changed all user names from swhiting to steven.whiting. Doesn’t change the login so can still use the old swhiting for logging in, was just easier. Despite the UPN changing, it oddly doesn’t update the Entra ID when it syncs to Azure. Still looking into that, but this ended up being for the best. Cause of the way our domain was originally setup you have to sign in as swhiting@stevenwhiting.co.uk so having to do the full steven.whiting@ would be annoying.
<#!
.SYNOPSIS
Update UPN and Primary SMTP Address for specific users identified by sAMAccountName (hybrid AD + Entra ID).
.DESCRIPTION
- Targets ONLY the sAMAccountNames you specify (hard list below).
- For each user, builds the new identity as firstname.surname@stevenwhiting.co.uk
based on their AD GivenName + Surname (lowercased; non-alphanumerics removed).
- Updates on-prem AD so hybrid sync flows changes to Entra ID/Exchange Online:
* userPrincipalName (UPN)
* mail (primary SMTP)
* proxyAddresses (sets new Primary as 'SMTP:' and preserves old sAMAccountName as alias 'smtp:')
* mailNickname (left part)
- Writes a dated CSV log for auditing and rollback.
- Includes rollback mode.
.PARAMETER AlsoSetExchangeOnline
Attempt to set primary SMTP in Exchange Online as well (OFF by default; hybrid sync usually overwrites EXO-only changes).
.PARAMETER RollbackPath
Path to a previous CSV log from this script to revert changes.
.NOTES
Author: Steven Whiting – Using Chat GPT so may not be perfect but it worked and did what I needed it to do. Probably better ways to do it. With this script you also have to manually edit the users you want changed. I quite liked that for safety.
Date: 2025-09-23
This script uses CmdletBinding(SupportsShouldProcess=$true).
Use -WhatIf on the script to preview, and -Confirm to be prompted.
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[switch]$AlsoSetExchangeOnline,
[string]$RollbackPath
)
Import-Module ActiveDirectory -ErrorAction Stop
# ---------- Helpers ----------
function Write-Info($msg) { Write-Host "[INFO ] $msg" -ForegroundColor Cyan }
function Write-Warn($msg) { Write-Warning $msg }
function Write-Err ($msg) { Write-Host "[ERROR] $msg" -ForegroundColor Red }
function Get-PrimarySmtpFromProxies([string[]]$Proxies) {
if (-not $Proxies) { return $null }
$p = $Proxies | Where-Object { $_ -cmatch '^SMTP:' } | Select-Object -First 1
if ($p) { return ($p -replace '^SMTP:','') }
return $null
}
function Build-NewAddressParts([string]$GivenName,[string]$Surname,[string]$Domain) {
if ([string]::IsNullOrWhiteSpace($GivenName) -or [string]::IsNullOrWhiteSpace($Surname)) {
throw "GivenName/Surname missing; cannot build firstname.surname."
}
$fn = ($GivenName -replace "[^A-Za-z0-9]", "").ToLower()
$sn = ($Surname -replace "[^A-Za-z0-9]", "").ToLower()
$local = ("{0}.{1}" -f $fn, $sn)
return @{ Local=$local; Upn=("{0}@{1}" -f $local,$Domain); Primary=("{0}@{1}" -f $local,$Domain); Nick=$local }
}
function Ensure-UniqueUpn([string]$CandidateUpn,[string]$DomainNC,[string]$UserDN) {
$exists = Get-ADUser -Filter ("userPrincipalName -eq '{0}'" -f $CandidateUpn) -SearchBase $DomainNC -ErrorAction SilentlyContinue
if (-not $exists -or ($exists.DistinguishedName -eq $UserDN)) { return $CandidateUpn }
$prefix,$suffix = $CandidateUpn.Split('@')
for ($i=1; $i -lt 1000; $i++) {
$try = "{0}{1}@{2}" -f $prefix,$i,$suffix
$exists = Get-ADUser -Filter ("userPrincipalName -eq '{0}'" -f $try) -SearchBase $DomainNC -ErrorAction SilentlyContinue
if (-not $exists -or ($exists.DistinguishedName -eq $UserDN)) { return $try }
}
throw "Unable to find a unique UPN after 999 attempts for $CandidateUpn"
}
function Set-UserMailAttributes {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Microsoft.ActiveDirectory.Management.ADUser]$User,
[string]$NewUpn,
[string]$NewPrimarySmtp,
[string]$NewMailNick,
[string]$DomainSuffix
)
$dn = $User.DistinguishedName
$currentProxies = @($User.proxyAddresses)
# Demote any existing primary entries to alias form (lowercase)
$proxiesNoPrimary = @()
foreach ($p in $currentProxies) { $proxiesNoPrimary += ($p -replace '^SMTP:','smtp:') }
# Add old sAMAccountName as alias
$newAliasAdded = "$($User.SamAccountName)@$DomainSuffix"
if ($proxiesNoPrimary -notcontains ("smtp:$newAliasAdded")) {
Write-Info "Adding old sAMAccountName as alias: smtp:$newAliasAdded"
$proxiesNoPrimary += "smtp:$newAliasAdded"
}
# Remove duplicates and any lingering entry that collides with new primary
$proxiesNoPrimary = $proxiesNoPrimary | Sort-Object -Unique | Where-Object { $_ -ne ("smtp:$NewPrimarySmtp") -and $_ -ne ("SMTP:$NewPrimarySmtp") }
# Final list: new primary first, then aliases
$finalProxies = @("SMTP:$NewPrimarySmtp") + $proxiesNoPrimary
# Ensure array of strings
if ($finalProxies -isnot [array]) { $finalProxies = @($finalProxies) }
$finalProxies = $finalProxies | ForEach-Object { [string]$_ }
$replace = @{
proxyAddresses = $finalProxies
mail = $NewPrimarySmtp
mailNickname = $NewMailNick
}
if ($PSCmdlet.ShouldProcess($User.SamAccountName, "Set mail attributes and UPN")) {
Set-ADUser -Identity $dn -UserPrincipalName $NewUpn -Replace $replace -ErrorAction Stop
}
return @{
NewProxies = $finalProxies
NewAliasAdded = "smtp:$newAliasAdded"
}
}
# ---------- Config ----------
$DomainSuffix = 'stevenwhiting.co.uk'
$DomainNC = 'DC=stevenwhiting,DC=co,DC=uk'
$SearchBaseDN = 'OU=IT,OU=formation,OU=.Users,OU=Steven Whiting,DC=stevenwhiting,DC=co,DC=uk'
$SamAccountNames = @('jwhiting','ldave','swhiting')
# ---------- Resolve targets ----------
$ResolvedUsers = @()
foreach ($sam in $SamAccountNames) {
$found = Get-ADUser -Filter "SamAccountName -eq '$sam'" -SearchBase $SearchBaseDN -Properties GivenName,Surname,mail,mailNickname,proxyAddresses,DistinguishedName,SamAccountName,userPrincipalName
if (-not $found) { Write-Err "User not found in OU scope: $sam"; continue }
if ($found.Count -gt 1) { Write-Err "Multiple matches for sAMAccountName $sam in the scope. Aborting for safety."; exit 1 }
$ResolvedUsers += $found
}
if ($ResolvedUsers.Count -ne $SamAccountNames.Count) {
Write-Err "Expected to resolve $($SamAccountNames.Count) users, but resolved $($ResolvedUsers.Count). Aborting."
return
}
# Confirmation
Write-Host "The following users will be updated:" -ForegroundColor Yellow
$ResolvedUsers | Select-Object SamAccountName, UserPrincipalName, GivenName, Surname, DistinguishedName | Format-Table -AutoSize
$ans = Read-Host "Type YES to proceed (anything else aborts)"
if ($ans -ne 'YES') { Write-Host 'Aborted by operator.'; return }
# ---------- Processing ----------
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$LogPath = Join-Path -Path (Get-Location) -ChildPath ("UPN_SMTP_Change_Log_{0}.csv" -f $timestamp)
$logRows = @()
foreach ($user in $ResolvedUsers) {
try {
$parts = Build-NewAddressParts -GivenName $user.GivenName -Surname $user.Surname -Domain $DomainSuffix
$targetUpn = Ensure-UniqueUpn -CandidateUpn $parts.Upn -DomainNC $DomainNC -UserDN $user.DistinguishedName
$oldPrimarySmtp = Get-PrimarySmtpFromProxies $user.proxyAddresses
if (-not $oldPrimarySmtp -and $user.mail) { $oldPrimarySmtp = $user.mail }
# Skip if already changed
if ($user.UserPrincipalName -eq $targetUpn -and $oldPrimarySmtp -eq $parts.Primary) {
Write-Host "$($user.SamAccountName) is already updated. Skipping..." -ForegroundColor Yellow
$logRows += [pscustomobject]@{
When = (Get-Date)
DN = $user.DistinguishedName
SamAccountName = $user.SamAccountName
DisplayName = $user.Name
OldUPN = $user.UserPrincipalName
NewUPN = $targetUpn
OldPrimarySMTP = $oldPrimarySmtp
NewPrimarySMTP = $parts.Primary
FinalProxyAddresses = ($user.proxyAddresses -join ';')
NewAliasAdded = "$($user.SamAccountName)@$DomainSuffix"
Result = "Already changed"
}
continue
}
Write-Info ("Processing {0}:" -f $user.SamAccountName)
$result = Set-UserMailAttributes -User $user -NewUpn $targetUpn -NewPrimarySmtp $parts.Primary -NewMailNick $parts.Nick -DomainSuffix $DomainSuffix
Write-Host " Final ProxyAddresses:"
foreach ($proxy in $result.NewProxies) {
if ($proxy -cmatch '^SMTP:') { Write-Host " $proxy (Primary)" -ForegroundColor Green }
else { Write-Host " $proxy (Alias)" -ForegroundColor DarkGray }
}
Write-Host " New alias added: $($result.NewAliasAdded)" -ForegroundColor Magenta
$logRows += [pscustomobject]@{
When = (Get-Date)
DN = $user.DistinguishedName
SamAccountName = $user.SamAccountName
DisplayName = $user.Name
OldUPN = $user.UserPrincipalName
NewUPN = $targetUpn
OldPrimarySMTP = $oldPrimarySmtp
NewPrimarySMTP = $parts.Primary
FinalProxyAddresses = ($result.NewProxies -join ';')
NewAliasAdded = $result.NewAliasAdded
Result = "Success"
}
}
catch {
Write-Err $_.Exception.Message
}
}
$logRows | Export-Csv -NoTypeInformation -Path $LogPath
Write-Info "Log written to: $LogPath"
Powershell – Update Direct Reports for a Manager in AD
Import-Module ActiveDirectory
$Manager = Get-ADUser -Identity "jdoe"
$Users = Import-CSV "C:\temp\DirectReports.csv"
foreach ($User in $Users) {
Set-ADUser -Identity $User.SamAccountName -Manager $Manager.DistinguishedName
}
Make the CSV
SamAccountName
swhiting
jwhiting
tnice