I wanted domain users on my order.realm network to have their own Windows account pictures automatically applied when they log on to a domain computer.
The environment in this example uses:
- Active Directory domain:
order.realm - Domain controller / file server:
Seido - Group Policy Management
- Windows domain-joined PCs
- A central PowerShell script stored in SYSVOL
- A scheduled task deployed through Group Policy
- Different profile pictures based on the logged-on username
The final result is that a user can log on to any computer covered by the GPO and Windows automatically generates and applies the correct account picture.
The final layout
The files are stored here:
\\Seido\SYSVOL\order.realm\scripts\
The main script is:
\\Seido\SYSVOL\order.realm\scripts\Set-AccountPicture.ps1
Profile images are stored beneath it:
\\Seido\SYSVOL\order.realm\scripts\AccountPictures\
For example:
AccountPictures
├── TuroTgV1.png
├── johnny_cage.png
├── mileena.png
├── kitana.png
├── scorpion.png
├── coin_ops.png
└── raiden.png
The scheduled task deployed through Group Policy calls the PowerShell script directly from SYSVOL.
The exported task confirmed that the working configuration runs as NT AUTHORITY\SYSTEM, uses the highest available privileges, has an At log on trigger with a 30-second delay, and launches PowerShell from the direct \\Seido\SYSVOL... path.
Why a scheduled task is needed
Windows account pictures are registered under:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\<SID>
Because this is under HKLM, an ordinary domain user normally cannot write the required values.
The solution was therefore to run the script as:
NT AUTHORITY\SYSTEM
with:
Run with highest privileges
The task runs when somebody logs on.
This lets the script modify the local computer while still identifying which interactive user has just logged on.
Group Policy layout
I created a dedicated GPO called:
OrderRealm Account Picture
Rather than mixing this with my Folder Redirection and Drive Maps policy, the account-picture policy is kept separate.
The GPO is linked to:
order.realm
└── NetherRealm
└── Computers
This is important.
Because the scheduled task is under Computer Configuration, the GPO must apply to the computer account, not simply the user account.
For example, one of my computers is:
CN=RACING-RIG,
OU=Computers,
OU=NetherRealm,
DC=order,
DC=realm
Once the GPO was correctly linked to the Computers OU, this appeared in:
gpresult /r /scope computer
under:
Applied Group Policy Objects
-----------------------------
Computer Configs
Apps
OrderRealm Account Picture
Default Domain Policy
That was the key confirmation that the computer was finally receiving the policy.
Creating the scheduled task
Edit:
OrderRealm Account Picture
and go to:
Computer Configuration
→ Preferences
→ Control Panel Settings
→ Scheduled Tasks
Create:
New
→ Scheduled Task (At least Windows 7)
General
Use:
Name:
Set OrderRealm Account Picture
User account:
NT AUTHORITY\SYSTEM
Enable:
Run whether user is logged on or not
Run with highest privileges
The working exported task uses SYSTEM with the highest run level.
Trigger
Create an:
At log on
trigger.
Use:
Any user
and add:
Delay task for: 30 seconds
The delay became useful because the script needs an interactive Windows session to exist before it can determine which user has logged on. The exported working task contains a PT30S delay.
Action
Program:
powershell.exe
Arguments:
-ExecutionPolicy Bypass -File "\\Seido\SYSVOL\order.realm\scripts\Set-AccountPicture.ps1"
I originally used the domain DFS-style path:
\\order.realm\SYSVOL\order.realm\scripts\
but in this environment the direct server path proved more reliable:
\\Seido\SYSVOL\order.realm\scripts\
The final exported task uses this direct path.
Detecting the logged-on user
One of the more interesting problems was identifying the user while the script itself was running as SYSTEM.
Initially I used:
(Get-CimInstance Win32_ComputerSystem).UserName
This worked on one machine but returned nothing on another.
The log showed:
Running as: NT AUTHORITY\SYSTEM
ERROR: No logged-on user found.
The more reliable method was to find explorer.exe and ask Windows who owns that process.
Because Explorer normally belongs to the interactive desktop user, this provides the actual logged-on domain account.
The script waits for Explorer to appear and retries for up to 60 seconds.
Mapping users to pictures
The script can use one central configuration to decide which picture belongs to which user.
For example:
$PictureFile = switch ($Username.ToLower()) {
"turokhan" { "TuroTgV1.png" }
"luke" { "johnny_cage.png" }
"amanda" { "mileena.png" }
"emma" { "kitana.png" }
"scorpion" { "scorpion.png" }
"lamb" { "coin_ops.png" }
default { $null }
}
That means every computer can run exactly the same script.
The script simply determines who is logged on and selects the matching image.
To add another user later, I only need to:
- Put their PNG in
AccountPictures. - Add one line to the switch statement.
For example:
"raiden" { "raiden.png" }
Why the script generates several JPEG files
Simply pointing every Windows account-picture registry value at the original PNG initially resulted in the image not appearing correctly.
Looking at Windows’ existing account-picture registry structure showed that Windows normally keeps several different image sizes.
The working solution therefore generates:
Image32.jpg
Image40.jpg
Image48.jpg
Image64.jpg
Image96.jpg
Image192.jpg
Image208.jpg
Image240.jpg
Image424.jpg
Image448.jpg
Image1080.jpg
These are stored under:
C:\Users\Public\AccountPictures\<USER-SID>\
For example:
C:\Users\Public\AccountPictures\
S-1-5-21-1646595580-629131343-3901383525-1107\
The registry then points each Windows image size to the corresponding JPEG.
For example:
Image32
→ C:\Users\Public\AccountPictures\<SID>\Image32.jpg
Image96
→ C:\Users\Public\AccountPictures\<SID>\Image96.jpg
Image448
→ C:\Users\Public\AccountPictures\<SID>\Image448.jpg
Image1080
→ C:\Users\Public\AccountPictures\<SID>\Image1080.jpg
Once this structure was used, the account picture displayed correctly.
Final PowerShell script
The following is the structure of the working multi-user script.
$LogFolder = "C:\ProgramData\OrderRealm"
$LogFile = "$LogFolder\SetAccountPicture.log"
New-Item -Path $LogFolder -ItemType Directory -Force | Out-Null
try {
"----- $(Get-Date) -----" | Out-File $LogFile -Append
"Running as: $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" |
Out-File $LogFile -Append
# Find the interactive logged-on user through Explorer
$LoggedOnUser = $null
for ($i = 0; $i -lt 12; $i++) {
$Explorer = Get-CimInstance Win32_Process -Filter "Name='explorer.exe'" |
Select-Object -First 1
if ($Explorer) {
$Owner = Invoke-CimMethod `
-InputObject $Explorer `
-MethodName GetOwner
if ($Owner.User) {
$LoggedOnUser = "$($Owner.Domain)\$($Owner.User)"
break
}
}
Start-Sleep -Seconds 5
}
if (-not $LoggedOnUser) {
throw "No interactive logged-on user found after 60 seconds."
}
"Logged-on user: $LoggedOnUser" |
Out-File $LogFile -Append
$Domain, $Username = $LoggedOnUser -split "\\", 2
# Choose image for user
$PictureFile = switch ($Username.ToLower()) {
"turokhan" { "TuroTgV1.png" }
"luke" { "johnny_cage.png" }
"amanda" { "mileena.png" }
"emma" { "kitana.png" }
"scorpion" { "scorpion.png" }
"lamb" { "coin_ops.png" }
default { $null }
}
if (-not $PictureFile) {
throw "No account picture configured for user: $Username"
}
"Picture selected: $PictureFile" |
Out-File $LogFile -Append
# Get SID
$Account = New-Object System.Security.Principal.NTAccount($LoggedOnUser)
$SID = $Account.Translate(
[System.Security.Principal.SecurityIdentifier]
).Value
"SID: $SID" |
Out-File $LogFile -Append
# Source image
$Source = "\\Seido\SYSVOL\order.realm\scripts\AccountPictures\$PictureFile"
if (!(Test-Path $Source)) {
throw "Source image not found: $Source"
}
# Native Windows account picture folder
$DestFolder = "C:\Users\Public\AccountPictures\$SID"
New-Item `
-Path $DestFolder `
-ItemType Directory `
-Force `
-ErrorAction Stop | Out-Null
Add-Type -AssemblyName System.Drawing
$SourceImage = [System.Drawing.Image]::FromFile($Source)
$Sizes = 32,40,48,64,96,192,208,240,424,448,1080
$RegPath =
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\$SID"
New-Item `
-Path $RegPath `
-Force `
-ErrorAction Stop | Out-Null
foreach ($Size in $Sizes) {
$Dest = Join-Path $DestFolder "Image$Size.jpg"
$Bitmap =
New-Object System.Drawing.Bitmap($Size, $Size)
$Graphics =
[System.Drawing.Graphics]::FromImage($Bitmap)
$Graphics.InterpolationMode =
[System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic
$Graphics.SmoothingMode =
[System.Drawing.Drawing2D.SmoothingMode]::HighQuality
$Graphics.PixelOffsetMode =
[System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality
$Graphics.DrawImage(
$SourceImage,
0,
0,
$Size,
$Size
)
$Bitmap.Save(
$Dest,
[System.Drawing.Imaging.ImageFormat]::Jpeg
)
$Graphics.Dispose()
$Bitmap.Dispose()
New-ItemProperty `
-Path $RegPath `
-Name "Image$Size" `
-Value $Dest `
-PropertyType String `
-Force `
-ErrorAction Stop | Out-Null
}
$SourceImage.Dispose()
"Account picture images generated successfully." |
Out-File $LogFile -Append
"SUCCESS" |
Out-File $LogFile -Append
exit 0
}
catch {
"ERROR: $($_.Exception.Message)" |
Out-File $LogFile -Append
exit 10
}
Useful troubleshooting commands
These were the commands that proved most useful while getting this working.
Check whether the GPO applies to the computer
gpresult /r /scope computer
Look for:
OrderRealm Account Picture
under:
Applied Group Policy Objects
If it isn’t there, there is no point troubleshooting the PowerShell script yet.
The computer isn’t receiving the policy.
Force Group Policy processing
gpupdate /force
If Folder Redirection is also configured, Windows may report that some user policy requires a logoff.
That is unrelated to the account-picture script itself.
Check whether the scheduled task exists
Get-ScheduledTask -TaskName "Set OrderRealm Account Picture"
If Windows says no scheduled task was found, check the GPO scope/link before touching the script.
Run the task manually
This became the quickest way to test changes:
Start-ScheduledTask -TaskName "Set OrderRealm Account Picture"
Because the task reads the script directly from:
\\Seido\SYSVOL\order.realm\scripts\
changing the PowerShell script itself does not require a new gpupdate.
Just save the script and run the existing task again.
Check the task result
Get-ScheduledTaskInfo -TaskName "Set OrderRealm Account Picture" |
Select-Object LastRunTime, LastTaskResult
A successful execution should return:
LastTaskResult
0
The script deliberately exits with:
10
when an exception occurs.
Read the diagnostic log
Get-Content "C:\ProgramData\OrderRealm\SetAccountPicture.log" -Tail 20
A successful run looks roughly like:
Running as: NT AUTHORITY\SYSTEM
Logged-on user: ORDERREALM\turokhan
Picture selected: TuroTgV1.png
SID: S-1-5-21-...
Account picture images generated successfully.
SUCCESS
This log was invaluable because a scheduled task can otherwise fail without giving much useful information.
Check generated images
$SID = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value
Get-ChildItem "C:\Users\Public\AccountPictures\$SID"
A working deployment should show all of the generated JPEG sizes.
Check the registry
$SID = ([System.Security.Principal.WindowsIdentity]::GetCurrent()).User.Value
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\$SID"
Each entry should point to the corresponding JPEG under:
C:\Users\Public\AccountPictures\<SID>\
Things that caught me out
There were several useful lessons from this project.
User Configuration vs Computer Configuration
The first scheduled task was placed under User Configuration.
That made the setup confusing because the task itself needed to operate as SYSTEM.
Moving it into a dedicated GPO under:
Computer Configuration
and linking that GPO to the computer OU made the design much easier to understand.
A GPO can look correct but still not be applying
At one stage the GPO link looked correct in Group Policy Management, but:
gpresult /r /scope computer
did not show it.
After forcing:
gpupdate /force
the GPO appeared and the scheduled task was subsequently created.
The lesson is simple:
Trust gpresult on the client more than appearances in GPMC.
SYSTEM is not the logged-on user
When PowerShell runs as SYSTEM:
[System.Security.Principal.WindowsIdentity]::GetCurrent()
returns:
NT AUTHORITY\SYSTEM
It cannot simply be used to determine the domain user.
Using the owner of explorer.exe solved that problem.
Account pictures are more than one image
Copying a PNG wasn’t enough.
Windows expects several account-picture sizes and registry references.
Generating the full JPEG set under:
C:\Users\Public\AccountPictures\<SID>
produced the reliable result.
Use a log file from the beginning
The final script writes to:
C:\ProgramData\OrderRealm\SetAccountPicture.log
That makes troubleshooting dramatically easier.
Instead of wondering whether the task started, which user it detected, which file it selected, or whether registry creation failed, all of those details are recorded.
Final result
The finished arrangement is now centrally managed.
When a supported domain user logs on:
Domain user logs on
↓
Computer receives OrderRealm Account Picture GPO
↓
Scheduled task waits 30 seconds
↓
Task runs PowerShell as SYSTEM
↓
Script finds interactive user
↓
Username selects matching PNG
↓
PNG is read from SYSVOL
↓
Windows-size JPEGs are generated locally
↓
AccountPicture registry values are created
↓
Windows displays the user's custom profile picture
The nice part is that no individual PC needs to know which user belongs to which image.
Everything is controlled from one PowerShell script and one central image directory.
Adding another user later is therefore simply a case of dropping in another image and adding another username-to-picture mapping.
For a home lab this is probably more elaborate than strictly necessary, but it also demonstrates quite nicely how Group Policy Preferences, scheduled tasks, SYSTEM context, Windows SIDs and SYSVOL can all be combined to centrally configure domain PCs.

