batch-filecmdwiresharksccmappdata

How to get to users %APPDATA% when running as system user from a batch file


Deploying applications via SCCM. Currently trying to deploy Wireshark and I am 1st trying to create the directory then copy over a preference file to the users %APPDATA%. The preference file basically stops the application from checking for auto updates.

Reason I need to create the directory is because it does not exist until Wireshark is launched for the 1st time.

Issue is that when doing this, SCCM is deploying as a system user so %APPDATA% goes to directory C:\Windows\System32\config\systemprofile\AppData\Roaming\

But I would like to get to C:\Users\SPECIFIC USER\AppData\Roaming

I am deploying the application with a batch file:


Wireshark-win64-2.4.6.exe /S

mkdir %APPDATA%\Wireshark\

xcopy preferences %APPDATA%\Wireshark

This will work locally on my own machine but if I run under PSEXEC (like SCCM would) then it will end up in the incorrect directory.

I am new to creating applications within SCCM as well as using batch files to deploy so please include details if possible. Regardless I appreciate the help!


Solution

  • Done with using getprofiles.cmd to echo the profiles and using main.cmd with a for loop to process the profile paths.

    main.cmd:

    @echo off
    setlocal
    
    :: Install Wireshark.
    echo Wireshark-win64-2.4.6.exe /S
    
    :: Update Wireshark app data in user profiles.
    for /f "tokens=*" %%A in ('getprofiles.cmd "\AppData\Roaming"') do (
        call :skip_profile "%%~A" "\\Administrator\\" "\\MSSQL\$SQLEXPRESS\\" || (
            echo mkdir "%%~A\Wireshark\"
            echo xcopy preferences "%%~A\Wireshark"
        )
    )
    
    exit /b
    
    :skip_profile
    for %%A in (%*) do (
        if not "%%~A" == "" if /i not "%%~A" == "%~1" (
            echo "%~1"| findstr /i "%%~A" >nul 2>nul
            if not errorlevel 1 (
                echo Skip account "%~1"
                exit /b 0
            )
        )
    )
    exit /b 1
    

    getprofiles.cmd:

    @echo off
    setlocal
    
    if "%~1" == "/?" goto :help
    
    :: ProfileList key that contains profile paths.
    set "ProfileListKey=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
    
    :: Get profiles directory path.
    set "ProfilesDirectory="
    
    for /f "tokens=1,3" %%A in (
        'reg query "%ProfileListKey%" /v "ProfilesDirectory"'
    ) do if /i "%%~A" == "ProfilesDirectory" call set "ProfilesDirectory=%%~B"
    
    if not defined ProfilesDirectory (
        >&2 echo ProfilesDirectory is undefined
        exit /b 1
    )
    
    :: Search all profile paths in profiles directory and echo existing paths appended with the 1st script argument.
    for /f "delims=" %%A in (
        'reg query "%ProfileListKey%"'
    ) do call :ProfilePath "%%~A" "%~1"
    
    exit /b
    
    :ProfilePath
    setlocal
    set "arg1=%~1"
    
    :: Validate 1st call argument is a profile subkey.
    if not defined arg1 exit /b 1
    if /i "%arg1%" == "%ProfileListKey%" exit /b 1
    if "%arg1:~,1%" == " " exit /b 1
    
    :: Echo existing profile paths with defined 2nd argument appended.
    for /f "tokens=1,3" %%A in (
        'reg query "%arg1%" /v ProfileImagePath^|find /i "%ProfilesDirectory%"'
    ) do (
        if "%%~A" == "ProfileImagePath" (
            if exist "%%~B%~2" echo "%%~B%~2"
        )
    )
    exit /b
    
    :help
    echo Prints profile paths from the registry that exist in the Profiles Directory.
    echo 1st argument can be a path to be appended to the profile path.
    echo   i.e. "\AppData\Roaming" is appended to become "C:\Users\...\AppData\Roaming".
    exit /b
    

    The script main.cmd echoes the results for testing. Remove the echoes to actually use if commands are valid.

    The ProfileList key in the registry stores the path to find the profiles and has subkeys with data such as path of each profile on the machine.

    main.cmd can avoid profiles such as Administrator and MSSQL$SQLEXPRESS. The called label :skip_profile takes the profile path as 1st argument. Following arguments are for patterns and if matched, will be a skipped profile. findstr is being used for checking the profile path for a match with regular expressions so use findstr /? for syntax requirements. Case is set as insensitive as to use of /i.

    The getprofiles.cmd script gets the ProfilesDirectory path which is where the user profile folders can be found. It then querys the key to get the profile keys by using the called label of :ProfilePath. The label checks if the ProfilesDirectory path is found in each profile path found. It then checks if the path exists before echoing the path. If the optional 1st parameter is passed, then it will be appended and the path will be validated as that path.

    A test outputs:

    Wireshark-win64-2.4.6.exe /S
    mkdir "C:\Users\Michael\AppData\Roaming\Wireshark\"
    xcopy preferences "C:\Users\Michael\AppData\Roaming\Wireshark"
    

    which seems OK as I only have 1 user profile on my current machine.

    You could probably merge the code together to make just 1 script though I decided to leave getprofiles.cmd as reusable for other uses.