Jump to content

set User Account Picture


Go to solution Solved by JLogan3o13,

Recommended Posts

Posted

Maybe is not this big life issue, but I want to change the Windows 10/11 current user account picture with a script.

Not so simple but doable, I took ideas from:

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3f50b25a-4c47-4bc8-959e-1108419288a7/how-to-set-specific-users-account-picture-from-cmd?forum=scripting

So I assembled a testbed scritpt to  prepare the avatar images in various sizes, and to modify the registry, finding the right user SID and right keys to modify.

All seems to work, even regwrite instruction results OK (1), but no changes in the registry.

So I found a showstopper problem:

this registry key:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users contains the  definitions of the images for the various users (take a look at attached screenshoot).

But I am not able to modify that key or subkeys and neither modify permissions also with an admin, because it seems only the good old "Administrator" (disabled by default in modern OSes) is able to change that permissions.

I say this because I reactivated manually the Administrator, but in some environments (corporate domains) it is not so simple.

Ideas ? I hate to automate the windows gui for a task like this 😒.

regedit_UPninKc2g8.png

Posted

Good point, is only because I am searching for a "universal" method to set the user account image, we have also hundreds of non domain users.

Via windows gui is something doable without special permission, and if I well understand the logic under the hood has been hardened only after the very first versions of win10, back in 2015. In windows you can do a lot of things via cmd/shell and not being able to do a simple task like this drives me crazy.

Update, also the images.jpg in \useraccountpictures folder are protected with the same (scarce) permissions of the related registry keys.

Posted

You can set property "thumbnailPhoto" for domain users using the AD UDF.
Details can be found on the forum. In this and further threads.

My UDFs and Tutorials:

Spoiler

UDFs:
Active Directory (NEW 2024-07-28 - Version 1.6.3.0) - Download - General Help & Support - Example Scripts - Wiki
ExcelChart (2017-07-21 - Version 0.4.0.1) - Download - General Help & Support - Example Scripts
OutlookEX (2021-11-16 - Version 1.7.0.0) - Download - General Help & Support - Example Scripts - Wiki
OutlookEX_GUI (2021-04-13 - Version 1.4.0.0) - Download
Outlook Tools (2019-07-22 - Version 0.6.0.0) - Download - General Help & Support - Wiki
PowerPoint (2021-08-31 - Version 1.5.0.0) - Download - General Help & Support - Example Scripts - Wiki
Task Scheduler (2022-07-28 - Version 1.6.0.1) - Download - General Help & Support - Wiki

Standard UDFs:
Excel - Example Scripts - Wiki
Word - Wiki

Tutorials:
ADO - Wiki
WebDriver - Wiki

 

  • Moderators
  • Solution
Posted (edited)
25 minutes ago, t0nZ said:

I am searching for a "universal" method to set the user account image, we have also hundreds of non domain users.

Well if they are non-domain, are the computers company-owned or BYOD? If company-owned, you're going to be limited to PSExec running as SYSTEM, and are going to run into issues connecting to them over the open internet. If BYOD, you're out of luck as you don't have permission to do anything on those devices.

Edited by JLogan3o13

"Profanity is the last vestige of the feeble mind. For the man who cannot express himself forcibly through intellect must do so through shock and awe" - Spencer W. Kimball

How to get your question answered on this forum!

Posted

Hi @JLogan3o13 you made my day! 

Quote

you're going to be limited to PSExec running as SYSTEM

So I implemented a solution using PsExec64.exe.

To recap, part of my work is to prepare machines before they go in  to the wild, both in domain and non domain, and I among various scripts I lacked something to set the avatar image.

I present to you a working prototype, very very raw but working, tested on both win10 and win11

#RequireAdmin
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=Icone\angryRabbit.ico
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
;UAP
;User Account Picture
;()NSC 2023

;notes
; only way to access 64-bit node in a 32-bit compiled script is using HKLM64

;Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users
;https://social.msdn.microsoft.com/Forums/sqlserver/en-US/3f50b25a-4c47-4bc8-959e-1108419288a7/how-to-set-specific-users-account-picture-from-cmd?forum=scripting
; https://imagemagick.org/script/command-line-processing.php

;permissions SETACL
;https://www.autoitscript.com/forum/topic/162656-change-registry-key-permissions/?do=findComment&comment=1182956

;interesting but not used
;https://woshub.com/how-to-set-windows-user-account-picture-from-active-directory/


#include <Security.au3>
#include <Array.au3>
#include <File.au3>
#include <String.au3>
#include <_Gollog.au3>

Global $tempfolder = "c:\temp", $folder4accountPictures = @UserProfileDir & "\" & "accountpictures"
Global $onlyexe4conversion = "magick.exe", $exe4conversion = $tempfolder & "\" & $onlyexe4conversion
Global $onlyexe4regperm = "setacl.exe", $exe4regperm = $tempfolder & "\" & $onlyexe4regperm
Global $onlyexe4PsExec = "psexec64.exe", $exe4PsExec = $tempfolder & "\" & $onlyexe4PsExec

Global $registrypath = "hklm64\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\"

Global $registrypath4SETACL = "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\"

Global $aSizes[11] = ["32x32", "40x40", "48x48", "64x64", "96x96", "192x192", "208x208", "240x240", "424x424", "448x448", "1080x1080"]

Global $sConsoleUser = _GetConsoleUser()
Gollog("Console User: " & $sConsoleUser & @CRLF)

Global $aUserProfile = _GetProfile($sConsoleUser)
Gollog("Console User Path: " & $aUserProfile[1][1] & @CRLF)

_ArrayDisplay($aUserProfile, "current USER")

Global $aexpprof = _StringExplode($aUserProfile[1][2], "\")

Global $CUreg = $aexpprof[UBound($aexpprof) - 1]
Gollog($CUreg & @CRLF)
MsgBox(64, "cureg", $CUreg)


;TEST all users
Global $allUserProfiles = _GetProfile()
_ArrayDisplay($allUserProfiles, "all USERS")
;Gollog(_Security__GetAccountSid($sConsoleUser))
;END TESTall users

createFolder4userPictures()

TempCreate()

SetRegPermissions()

GenPicS()


Func GenPicS()
    ;image prepare
    Local $sourceimg = FileOpenDialog("Select Image for you User Profile", @UserProfileDir & "\" & "downloads", "Images (*.png;*.jpg;*.jpeg;*.gif;*.bmp)", 1)
    Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
    Local $aPathSplit = _PathSplit($sourceimg, $sDrive, $sDir, $sFileName, $sExtension)

    ;registry prepare
    If RegDelete($registrypath & $CUreg) Then
        Gollog("regdelete ok " & @CRLF)
        Gollog($registrypath & $CUreg & @CRLF)
    Else
        Gollog("regdelete error " & @error & @CRLF)
        Gollog($registrypath & $CUreg & @CRLF)
    EndIf

    #cs
        If RegDelete("HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\S-1-5-21-2509262863-2173886183-2659496606-1001") Then
            Gollog("regdelete ok " & @CRLF)
            Gollog("HKLM64\" & @CRLF)
        Else
            Gollog("regdelete error " & @error & @CRLF)
            Gollog("HKLM64\" & @CRLF)
    EndIf
    #ce

    If RegWrite($registrypath & $CUreg) Then
        Gollog("regwrite root ok " & @CRLF)
    Else
        Gollog("regwrite root error " & @error & @CRLF)
    EndIf

    For $i = 0 To UBound($aSizes) - 1
        ;image conversion
        ;Gollog($exe4conversion & " " & $sourceimg & " -resize " & $aSizes[$i] & " " & $folder4accountPictures &"\"&$sFileName & "_" & $aSizes[$i] & $sExtension & @CRLF)
        ShellExecuteWait($exe4conversion, $sourceimg & " -resize " & $aSizes[$i] & " " & $folder4accountPictures & "\" & $sFileName & "_" & $aSizes[$i] & $sExtension)

        ;registry write
        Local $apartsize = _StringExplode($aSizes[$i], "x")
        If RegWrite($registrypath & $CUreg, "Image" & $apartsize[0], "REG_SZ", $folder4accountPictures & "\" & $sFileName & "_" & $aSizes[$i] & $sExtension) Then
            Gollog("regewrite image ok " & $aSizes[$i] & @CRLF)
            Gollog($registrypath & $CUreg & "  Image" & $apartsize[0] & "  REG_SZ  " & $folder4accountPictures & "\" & $sFileName & "_" & $aSizes[$i] & $sExtension)
        Else
            Gollog("regwrite image error " & @error & @CRLF)
            Gollog($registrypath & $CUreg & "  Image" & $apartsize[0] & "  REG_SZ  " & $folder4accountPictures & "\" & $sFileName & "_" & $aSizes[$i] & $sExtension)
        EndIf
    Next
EndFunc   ;==>GenPicS

Func SetRegPermissions()

    ;writing bat file
    Local $batfile = $tempfolder & "\" & "UAPsetacl.bat"
    If FileExists($batfile) Then
        FileDelete($batfile)
        gollog("deleted old " & $batfile)
    EndIf

    Local $hFileOpen = FileOpen($batfile, $FO_APPEND)
    If $hFileOpen = -1 Then
        MsgBox($MB_SYSTEMMODAL, "", "An error occurred whilst writing the UAPsetacl.bat file", 3)
        GOLLOG("An error occurred whilst writing the UAPsetacl.bat file")
    exit
    EndIf


    FileWriteLine($hFileOpen, 'c:\temp\setacl.exe -on "' & $registrypath4SETACL & $CUreg & '" -ot reg -actn ace -ace "n:Administrators;p:full"')

    FileClose($hFileOpen)

    ;launch permission set on registry
    ShellExecuteWait($exe4PsExec," -s " & $batfile)

    ;ShellExecuteWait($exe4regperm, '-on "hklm\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\S-1-5-21-2509262863-2173886183-2659496606-1001" -ot reg -actn setowner -ownr "n:utente')

    ;ShellExecuteWait($exe4regperm, 'hklm\SOFTWARE\Microsoft\Windows\CurrentVersion\AccountPicture\Users\S-1-5-21-2509262863-2173886183-2659496606-1001" -ot reg -actn ace -ace "n:utente;p:full')

EndFunc   ;==>SetRegPermissions

Func TempCreate()
    If Not FileExists($tempfolder) Then DirCreate($tempfolder)
    FileInstall("C:\nsc_TEST\resources\imagemagick\magick.exe", $exe4conversion, 1)
    FileInstall("C:\nsc_TEST\resources\SetACL\64 bit\setacl.exe", $exe4regperm, 1)
    FileInstall("C:\NSC_test\resources\PSExec\PsExec64.exe", $exe4PsExec, 1)
EndFunc   ;==>TempCreate

;Create folder for user pictures - cleaning pre existing
Func createFolder4userPictures()
    If FileExists($folder4accountPictures & "\") Then
        DirRemove($folder4accountPictures, 1)
    EndIf

    DirCreate($folder4accountPictures)
EndFunc   ;==>createFolder4userPictures



;Use WMI query to get user logged onto console
Func _GetConsoleUser()
    Local $objWMIService = ObjGet("winmgmts:{impersonationLevel=impersonate}!//.")
    Local $colUsers, $objUser
    Local $strAccount

    $colUsers = $objWMIService.InstancesOf("Win32_ComputerSystem")
    For $objUser In $colUsers
        $strAccount = $objUser.UserName
    Next

    If $strAccount <> "" Then
        Return $strAccount
    Else
        Return (1)
    EndIf
EndFunc   ;==>_GetConsoleUser

; #FUNCTION# ====================================================================================================================================
; Name...........: _GetProfile
; Description ...: Determine each user's Profile folder, the user's SID and if the profile is loaded to the registry
; Syntax.........: _GetProfile([$sAccount, $sComputer])
; Parameters ....: $sAccount - User account name, defaults to all users
;                  $sComputer - Computer name, the local computer is default
; Requirement(s).: Service 'RemoteRegistry' running on the target computer
;                  When the target computer is the local computer, the 'RemoteRegistry' service isn't required
; Return values .: An array containing the path to each user's profile folder, the user's SID
;                  The array returned is two-dimensional and is made up as follows:
;                  $array[0][0] = Number of profiles
;                  $array[1][0] = 1st user name
;                  $array[1][1] = Path to 1st user profile
;                  $array[1][2] = 1st user registry hive
;                  $array[1][3] = 1 if 1st user profile is loaded to the registry, 0 if not
;                  $array[2][0] = 2nd user name
;                  $array[2][1] = Path to 2nd user profile
;                  $array[2][2] = 2nd user registry hive
;                  $array[2][3] = 1 if 2nd user profile is loaded to the registry, 0 if not
;                  ...
;                  $array[n][0] = nth user name
;                  $array[n][1] = Path to nth user profile
;                  $array[n][2] = nth user registry hive
;                  $array[n][3] = 1 if nth user profile is loaded to the registry, 0 if not
; Author ........: engine
; Modified.......: AdamUL
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; _GetProfile("Default User") to get Default User's profile data on the local computer
; ===============================================================================================================================================

Func _GetProfile($sAccount = "", $sComputer = @ComputerName)
    Local $avArray[1][4], $sDefaultUser, $sEnv
    Local Const $sProfileListKey = "\\" & $sComputer & "\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
    Local Const $sRootKey = "\\" & $sComputer & "\HKEY_USERS\"
    Local Const $sDefaultUser1 = RegRead($sProfileListKey, "DefaultUserProfile")
    Local Const $iDefaultUser1Error = @error
    Local Const $sDefaultUser2 = RegRead($sProfileListKey, "Default")
    Local Const $iDefaultUser2Error = @error
    If $iDefaultUser1Error And $iDefaultUser2Error Then
        $avArray[0][0] = 0
        Return $avArray
    EndIf
    If $iDefaultUser1Error Then
        $sDefaultUser = "Default"
    Else
        $sDefaultUser = $sDefaultUser1
    EndIf

    If $sAccount = "" Or $sAccount = $sDefaultUser Then
        Local $iInstance, $sSID
        While 1
            $iInstance += 1
            $sSID = RegEnumKey($sProfileListKey, $iInstance)
            If @error Then ExitLoop
            If StringLen($sSID) > 8 Then ProfileAdd($avArray, $sSID, $sProfileListKey, $sRootKey)
        WEnd
        Local $u = UBound($avArray), $iSum
        For $k = 1 To $u - 1
            $iSum += $avArray[$k][3]
        Next
        ReDim $avArray[$u + 1][4]
        $avArray[$u][0] = $sDefaultUser
        $avArray[$u][1] = RegRead($sProfileListKey, "ProfilesDirectory") & "\" & $sDefaultUser
        If $iSum = 0 Then
            $avArray[$u][2] = "\\" & $sComputer & "\HKEY_CURRENT_USER"
            $avArray[$u][3] = 1
        Else
            Local $avDomain, $iN = 998, $sDSID, $avDU
            $avDomain = _Security__LookupAccountName($sComputer, $sComputer)
            Do
                $iN += 1
                $sDSID = $avDomain[0] & "-" & $iN
                $avDU = _Security__LookupAccountSid($sDSID, $sComputer)
            Until $avDU = 0
            $avArray[$u][2] = $sRootKey & $sDSID
            $avArray[$u][3] = 0
        EndIf
        If $sAccount = $sDefaultUser Then
            Local $avNew[2][4] = [["", "", "", ""], [$avArray[$u][0], $avArray[$u][1], $avArray[$u][2], $avArray[$u][3]]]
            $avArray = $avNew
        EndIf
    Else
        Local $avSID = _Security__LookupAccountName($sAccount, $sComputer)
        If $avSID = 0 Then
            $avArray[0][0] = 0
            Return $avArray
        Else
            ProfileAdd($avArray, $avSID[0], $sProfileListKey, $sRootKey)
        EndIf
    EndIf
    $avArray[0][0] = UBound($avArray) - 1
    For $j = 1 To $avArray[0][0]
        $sEnv = StringRegExp($avArray[$j][1], "\x25\S{1,128}\x25", 1)
        If Not @error Then $avArray[$j][1] = StringReplace($avArray[$j][1], $sEnv[0], EnvGet(StringReplace($sEnv[0], "%", "")))
    Next
    Return $avArray
EndFunc   ;==>_GetProfile

; #INTERNAL_USE_ONLY#============================================================================================================================
; Name...........: ProfileAdd
; Description ...: Add profile data to an array that will be returned by _GetProfile function
; Syntax.........: ProfileAdd($avArray, $sSID, $sProfileListKey, $sRootKey)
; Parameters ....: $avArray - Array
;                  $sSID - Account SID
;                  $sProfileListKey - Constant defined inside _GetProfile function
;                  $sRootKey - Constant defined inside _GetProfile function
; Requirement(s).:
; Return values .:
; Author ........: engine
; Modified.......: AdamUL
; Remarks .......: For internal use only
; Related .......:
; Link ..........;
; Example .......;
; ===============================================================================================================================================

Func ProfileAdd(ByRef $avArray, $sSID, Const $sProfileListKey, Const $sRootKey)
    Local $sPath, $i
    Local $asSplit = Split_sKey("\" & $sProfileListKey)
    Local $avUser = _Security__LookupAccountSid($sSID, $asSplit[0])
    If Not @error And $avUser <> 0 Then
        If $avUser[2] = 1 Then
            $sPath = RegRead($sProfileListKey & "\" & $sSID, "ProfileImagePath")
            If Not @error Then
                $i = UBound($avArray)
                ReDim $avArray[$i + 1][4]
                $avArray[$i][0] = $avUser[0]
                $avArray[$i][1] = $sPath
                $avArray[$i][2] = $sRootKey & $sSID
                RegEnumKey($sRootKey & $sSID, 1)
                If @error Then
                    $avArray[$i][3] = 0
                Else
                    $avArray[$i][3] = 1
                EndIf
            EndIf
        EndIf
    EndIf
EndFunc   ;==>ProfileAdd

; #INTERNAL_USE_ONLY#============================================================================================================================
; Name...........: Split_sKey
; Description ...: Splits $sKey between computername, username and keyname
; Syntax.........: Split_sKey($sKey)
; Parameters ....: $sKey - Reg function main key
; Requirement(s).:
; Return values .:
; Author ........: engine
; Modified.......:
; Remarks .......: For internal use only
; Related .......:
; Link ..........;
; Example .......;
; ===============================================================================================================================================

Func Split_sKey($sKey)
    Local $asArray[3]
    If StringInStr($sKey, "\\\") = 1 Then
        Local $asComputer = StringRegExp($sKey, "\\\\\\[^\\]*\\", 1)
        If Not @error Then
            $asArray[0] = StringTrimRight(StringTrimLeft($asComputer[0], 3), 1)
            $sKey = StringReplace($sKey, $asComputer[0], "\", 1)
            If Not StringInStr($sKey, "\\") = 1 Then $sKey = StringTrimLeft($sKey, 1)
        EndIf
    EndIf
    If $asArray[0] = "" Then $asArray[0] = @ComputerName
    If StringInStr($sKey, "\\") = 1 And Not StringInStr($sKey, "\\\") = 1 Then
        Local $asUser = StringRegExp($sKey, "\\\\[^\\]*\\", 1)
        If Not @error Then
            $asArray[1] = StringTrimRight(StringTrimLeft($asUser[0], 2), 1)
            $sKey = StringReplace($sKey, $asUser[0], "", 1)
        EndIf
    EndIf
    If Not (StringInStr($sKey, "\") = 1 Or StringInStr($sKey, "\", 0, -1) = StringLen($sKey) Or StringInStr($sKey, "\\")) Then
        $asArray[2] = $sKey
    EndIf
    Return $asArray
EndFunc   ;==>Split_sKey

The Func SetRegPermissions() is the key.

Ok it's all very ugly and now I have to clean the code and find better solution (launching a .bat file....)

@water Thank you, but for now I store the new avatar pictures in a new folder so no problems with permissions.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...