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
Hurricane Katrina Blog
I remember coming across this back when it was happening. A guy, running a data centre was staying in the building to keep it all up, from what I remember. He eventually got a web cam up to film the street below. The vids have gone but someone on reddit found the blog
https://interdictor.livejournal.com/2005/08/29
A snippet in case it ever disappears
“August 29th, 2005
Ok, lots of creaking now. The blinds are rattling. The trees in Lafayette Square behind us are all still standing. We’re getting very light power flickers now — infrequently. Not even enough to reset the computer, but just enough to dim the lights for a sec. I’m gonna step outside on the 11th floor balcony and get a feel for the wind.
For those of you who haven’t been reading this, I’m on the 10th/11th floors of a 27 floor high rise at 650 Poydras in downtown New Orleans. If you go to Google Earth or some other map program, we’re the big brown building between Poydras ave and Lafayette Square and between Camp Street and St. Charles Ave. We’re about 6 blocks from the river. We have a view (in the daytime) of the river, the twin spans over the river, the interstate, the Entergy building, the landmark building, the federal court of appeals building, a bunch of hotels, etc.
Welcome to ground zero.”
Display contents of a txt file in CMD
Use the type command
type filename.txt
Jellyfin Android App Stuck on Logo
Running it on CasaOS, which apparently uses docker images. Updated Jellyfin then the android app was just stuck at the logo. Cleared the app cache which fixed it.
Windows 10 official ISO vulnerable
Annoying that the arses at MS haven’t bothered to patch the Windows 10 ISOs to the Blacklotus Malware so the recent official ISOs are flagged when using Rufus to create an image. These are ISOs even from the official Microsoft Volume License ISOs. They are clearly attempted to force people move to Windows 11 because those ISOs don’t have the issues.

Theme change
Have switched themes to one of the inbuilt ones as the one I’ve been using for years went out of support years ago.
Project Zomboid – Thought of the day
Played this on and off since they started and its good.
This game made you realise why it would all end quite quickly. Fire. In the game you can break into other houses, watching out for the dead left inside. You can use the cooker if the electric is still on, but if you then have to bug out quickly and don’t get a chance to turn the cooker off and there was food in it, a fire starts and that’s it. If it doesn’t rain soon then that fire spreads. I’ve had games where it spread for several blocks, I used it to thin out the heard. With no fire brigade to put fires out, it can spread for several streets.
Made you realise the likes of The Walking Dead wouldn’t last, simply due to fires starting and spreading.
Disable Windows Defender permanently
Run gpedit from the start menu.
Go to Computer Configuration/Administrative Templates/Windows Components/Microsoft Defender Antivirus/
Set “Turn off Microsoft Defender Antivirus” to ENABLED. If you do this WON’T work, even with tamper protection off. Its supposed to enter a reg settings but doesn’t and when you open gpedit again you’ll see its not set again.
Instead I go to
Computer Configuration/Administrative Templates/Windows Components/Microsoft Defender Antivirus/Real-time Protection
And set “turn off real-time protection” to ENABLED. That seems to survive the settings change and turns of real time protection so you can do what you need when its incorrectly blocking files.