Home > Mac administration, macOS, Packaging > Creating local user accounts with pycreateuserpkg

Creating local user accounts with pycreateuserpkg

As part of setting up new Macs, you may want to add one or more local user accounts with a pre-determined password to those Macs. The reasons for this may include the following:

  • Setting up a local administrator account
  • Setting up a “loaner” user account for a pool of loaner laptops
  • Setting up a local user account that automatically logs at startup for a library kiosk
  • Setting up a generic “student” account for use in a school’s computer lab

Previously, it was possible to use the venerable CreateUserPkg utility to accomplish this goal, but the password scheme used by CreateUserPkg stopped working on macOS High Sierra. An alternative tool which works on macOS High Sierra is pycreateuserpkg, a Python script written by Greg Neagle which generates packages that create local user accounts when installed. For more information, see below the jump.

Using pycreateuserpkg:

1. Download pycreateuserpkg from GitHub using the link below:

https://github.com/gregneagle/pycreateuserpkg

2. Decide on the settings you want for the new user account, using the options available below:


Required User Options:
-n NAME, –name=NAME: User shortname. REQUIRED.
-u UID, –uid=UID: User uid. REQUIRED.
-p PASSWORD, –password=PASSWORD: User password. REQUIRED.
Note: If the password option is not selected, you will be prompted for a password for
the account during the package creation process.
Required Package Options:
-V VERSION, –version=VERSION: Package version number. REQUIRED.
-i IDENTIFIER, –identifier=IDENTIFIER: Package identifier. REQUIRED.
Optional User Options:
-f FULLNAME, –fullname=FULLNAME: User full name. Optional.
-g GID, –gid=GID: User GID, otherwise known as a group identifier. Optional.
-H HOME, –home=HOME: Path to user home directory. Optional.
-s SHELL, –shell=SHELL: User shell path. Optional.
-a, –admin: User account should be added to admin group.
-A, –autologin: User account should automatically login.

view raw

gistfile1.txt

hosted with ❤ by GitHub

As an example, let’s create an installer package which installs a local user account with the following characteristics:

Account’s shortname: kiosk
Account’s displayed name: Kiosk
UID: 604
Account shell: /bin/bash
Account should have admin rights: No
Account should auto-login: Yes

Since we also need to provide version and identifier information for the installer package, we’ll use the following characteristics:

Version number: 1.0
Package identifier: com.github.kiosk_user_setup

To create the installer package which installs the previously-defined local account, make sure you have a copy of pycreateuserpkg available, then use the command shown below:

/path/to/createuserpkg --name="kiosk" --uid=604 --fullname="Kiosk" --shell="/bin/bash" --version=1.0 --identifier="com.github.kiosk_user_setup" "Create Kiosk User Account.pkg" --autologin

Screen Shot 2017 12 23 at 9 24 30 PM

 

Since we have not defined what the account’s password will be as part of the command above, we will be prompted to enter the password then enter it again to verify:

Screen Shot 2017 12 23 at 9 26 05 PM

 

Screen Shot 2017 12 23 at 9 26 21 PM

 

That should generate a new installer package named Create Kiosk User Account.pkg.

Screen Shot 2017 12 23 at 9 27 52 PM 

Testing pycreateuserpkg-generated installers

Once the package has been built, test it by taking the pycreateuserpkg-generated installer package and install it on a Mac which does not have the local account set up on it. The end result should be that the local account is set up on the Mac and configured with the desired settings and account rights.

Screen Shot 2017 12 23 at 9 32 12 PM

Screen Shot 2017 12 23 at 9 32 16 PM

 

To give some additional examples, let’s create a local administrator account with the following characteristics:

Account’s shortname: admin
Account’s displayed name: Administrator
UID: 600
Account shell: /bin/bash
Account should have admin rights: Yes
Account should auto-login: No

Since we also need to provide version and identifier information for the installer package, we’ll use the following characteristics:

Version number: 1.0
Package identifier: com.github.admin_user_setup

To create the installer package which installs the previously-defined local account, make sure you have a copy of pycreateuserpkg available, then use the command shown below:

/path/to/createuserpkg --name="admin" --uid=600 --fullname="Administrator" --shell="/bin/bash" --admin --version=1.0 --identifier="com.github.admin_user_setup" "Create Local Admin User Account.pkg"

Screen Shot 2017 12 23 at 9 35 43 PM

 

 

Screen Shot 2017 12 23 at 9 36 15 PM

 

When the package is installed, an account like this should now appear in the Users & Groups preferences in System Preferences.

 

Screen Shot 2017 12 23 at 9 36 41 PM

Screen Shot 2017 12 23 at 9 36 44 PM

 

Finally, let’s create a standard user local account with the following characteristics:

Account’s shortname: standarduser
Account’s displayed name: Standard User
UID: 603
Account shell: /bin/bash
Account should have admin rights: No
Account should auto-login: No

Since we also need to provide version and identifier information for the installer package, we’ll use the following characteristics:

Version number: 1.0
Package identifier: com.github.standard_user_user_setup

To create the installer package which installs the previously-defined local account, make sure you have a copy of pycreateuserpkg available, then use the command shown below:

/path/to/createuserpkg --name="standarduser" --uid=603 --fullname="Standard User" --shell="/bin/bash" --version=1.0 --identifier="com.github.standard_user_user_setup" "Create Standard User User Account.pkg"

 

Screen Shot 2017 12 23 at 9 34 57 PM

 

Screen Shot 2017 12 23 at 9 30 59 PM

 

When the package is installed, an account like this should now appear in the Users & Groups preferences in System Preferences.

 

Screen Shot 2017 12 23 at 9 33 16 PM

Screen Shot 2017 12 23 at 9 33 21 PM

 

How pycreateuserpkg works

pycreateuserpkg works by creating a plist file for the specified local user account, which allows the account information to work on 10.8.x and later. The user’s account information is written to a plist file and stored in the directory listed below:

/private/var/db/dslocal/nodes/Default/users

Screen Shot 2017 12 23 at 9 00 05 PM

 

An example account plist is shown below:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&gt;
<plist version="1.0">
<dict>
<key>ShadowHashData</key>
<array>
<data>
YnBsaXN0MDDRAQJfEBRTQUxURUQtU0hBNTEyLVBCS0RGMtMDBAUGBwhUc2Fs
dFdlbnRyb3B5Wml0ZXJhdGlvbnNPECAN9JFNQar3istN4qimB4Tp0j1uGmV5
rEfe9qUoLohPOU8QgEiRp6xYOrYgjZfmAhVlPzNReytc906hcOH2ZbDcOtW8
0c/ElK3n4Y0mkKd4UGJxC4/aYvknVVaEAaEVedCcvhub00HzHir16l7gYNbU
/QWrEo0JnIt93OIm2/Sgfm49arOqgqXM4r6msfu2uF6F4jAlTYYXytd8mgWC
MejFXD/nEcEDCAsiKS42QWTnAAAAAAAAAQEAAAAAAAAACQAAAAAAAAAAAAAA
AAAAAOo=
</data>
</array>
<key>_writers_UserCertificate</key>
<array>
<string>username</string>
</array>
<key>_writers_hint</key>
<array>
<string>username</string>
</array>
<key>_writers_jpegphoto</key>
<array>
<string>username</string>
</array>
<key>_writers_passwd</key>
<array>
<string>username</string>
</array>
<key>_writers_picture</key>
<array>
<string>username</string>
</array>
<key>_writers_realname</key>
<array>
<string>username</string>
</array>
<key>authentication_authority</key>
<array>
<string>;ShadowHash;HASHLIST:&lt;SALTED-SHA512-PBKDF2&gt;</string>
</array>
<key>generateduid</key>
<array>
<string>CB18DA2D-D70A-429D-9E76-413C5D0A54A2</string>
</array>
<key>gid</key>
<array>
<string>20</string>
</array>
<key>home</key>
<array>
<string>/Users/username</string>
</array>
<key>name</key>
<array>
<string>username</string>
</array>
<key>passwd</key>
<array>
<string>********</string>
</array>
<key>realname</key>
<array>
<string>User Name</string>
</array>
<key>shell</key>
<array>
<string>/bin/bash</string>
</array>
<key>uid</key>
<array>
<string>602</string>
</array>
</dict>
</plist>

view raw

gistfile1.txt

hosted with ❤ by GitHub

If the auto-login option is selected, an additional /etc/kcpassword file is also installed to facilitate the auto-login process.

Screen Shot 2017 12 23 at 9 00 11 PM

If the auto-login option is not selected, the /etc/kcpassword file will not be installed.

Screen Shot 2017 12 23 at 9 41 00 PM

Once the necessary file(s) are created by pycreateuserpkg, the utility then generates an installer package and post-installation script to install the account’s plist and the /etc/kcpassword file (if needed) into their proper places. An example postinstall script from a pycreateuserpkg-generated installer package is shown below:


#!/bin/bash
#
# postinstall for local account install
PlistArrayAdd() {
# Add $value to $array_name in $plist_path, creating if necessary
local plist_path="$1"
local array_name="$2"
local value="$3"
local old_values
local item
old_values=$(/usr/libexec/PlistBuddy -c "Print :$array_name" "$plist_path" 2>/dev/null)
if [[ $? == 1 ]]; then
# Array doesn't exist, create it
/usr/libexec/PlistBuddy -c "Add :$array_name array" "$plist_path"
else
# Array already exists, check if array already contains value
IFS=$'\012'
for item in $old_values; do
unset IFS
if [[ "$item" =~ ^\ *$value$ ]]; then
# Array already contains value
return 0
fi
done
unset IFS
fi
# Add item to array
/usr/libexec/PlistBuddy -c "Add :$array_name: string \"$value\"" "$plist_path"
}
ACCOUNT_TYPE=ADMIN # Used by read_package.py.
PlistArrayAdd "$3/private/var/db/dslocal/nodes/Default/groups/admin.plist" users "username" && \
PlistArrayAdd "$3/private/var/db/dslocal/nodes/Default/groups/admin.plist" groupmembers "CB18DA2D-D70A-429D-9E76-413C5D0A54A2"
if [ "$3" == "/" ]; then
# we're operating on the boot volume
# kill local directory service so it will see our local
# file changes — it will automatically restart
/usr/bin/killall DirectoryService 2>/dev/null || /usr/bin/killall opendirectoryd 2>/dev/null
fi
exit 0

view raw

gistfile1.txt

hosted with ❤ by GitHub

Note: The postinstall script may have different functionality, depending on which options are selected. For example, this is the postinstall script generated for an installer package which installs a standard user account which is set to auto-login:


#!/bin/bash
#
# postinstall for local account install
if [ "$3" == "/" ] ; then
# work around path issue with 'defaults'
/usr/bin/defaults write "/Library/Preferences/com.apple.loginwindow" autoLoginUser "kiosk"
else
/usr/bin/defaults write "$3/Library/Preferences/com.apple.loginwindow" autoLoginUser "kiosk"
fi
/bin/chmod 644 "$3/Library/Preferences/com.apple.loginwindow.plist"
if [ "$3" == "/" ]; then
# we're operating on the boot volume
# kill local directory service so it will see our local
# file changes — it will automatically restart
/usr/bin/killall DirectoryService 2>/dev/null || /usr/bin/killall opendirectoryd 2>/dev/null
fi
exit 0

view raw

gistfile1.txt

hosted with ❤ by GitHub

Once the pycreateuserpkg-generated package is installed, the account’s file(s) are put into the necessary places and the postinstall script handles any necessary preparation needed for the account to work properly.

  1. Doug
    January 5, 2018 at 9:44 pm

    I’ve tried using this but the computer doesn’t recognize the password I’ve set after package creation.

  2. Jason H
    February 19, 2018 at 3:34 pm

    I’ve create 5 pkgs with five different user names and UIDs. When I install the 3rd pkg, it adds the 3rd account but the first account is removed but the home folder remains. The account is gone from the GUI and when viewing users with dscl. When I run the fourth pkg, then the 2nd account disappears. (Only tested with Sierra 10.12.6)

    It might be related to not waiting long enough for the DirectoryService to start again. Has anyone else seen this issue?

    • Kevin Kolcz
      August 6, 2018 at 4:20 pm

      Did you ever find a solution to that issue? I am trying to setup something with multiple admin accounts now and having the same issue.

  3. March 28, 2018 at 3:07 pm

    This worked perfectly for me. I was wondering, though, is there a way to modify this to instead prepare the Mac to automatically log in to a AD account? I know we would have to send a binding command a head of the account creation PKG, but it seems like some of this script should be reusable for that.

    Thoughts or advice? This is just beyond my skill set to decipher.

  4. Peter Trondsen
    September 8, 2018 at 3:30 pm

    This is a great script, especially now with Thin Imaging upon us. Question, is there a way to customize the User Library, in a post install? Say I have a Library for an admin user I want to use.

  5. Jon Taylor
    November 29, 2018 at 3:02 am

    Are you aware of any issues with this software regarding users with uids below 500 (say 499) and Mojave? I’ve always created a hidden admin with uid 499, but it no longer works when installed on a fresh copy of Mojave. Same install package with a uid above 500 works just fine. I’m thinking there are some new uid restrictions with Mojave, but still not sure.

    • Peter Trondsen
      November 29, 2018 at 1:58 pm

      Hey, I need to test that, I wasn’t aware of this issue. I have a Mac that I upgraded to Mojave that has a hidden admin at 489 user id, which works, but I haven’t tried on a straight install. I will try and reply back.
      Thanks

      • Jon Taylor
        November 29, 2018 at 3:20 pm

        Definitely curious to see what you find out. Same thing with me, users below 500 that were upgraded to Mojave work fine, but not fresh installs. The package installs fine on a fresh copy, but when I go to authenticate as an admin it just shakes at me.

      • Jon Taylor
        November 30, 2018 at 3:26 am

        Did some more testing and uid below 500 doesn’t seem to be an issue. Not sure what was causing me issues previously, but I’ve gotten it working now.

  6. Tobias Hall
    December 3, 2018 at 2:12 pm

    I’m trying to run this on Mojave but the –autlogin doesn’t seem to work. Was just wondering if it still works or broke with 10.14.1?

  7. March 21, 2019 at 11:47 pm

    Having a problem with saving the file to my desktop, I get an error that says Must provide exactly one file name. Also, what exactly should the identifier be?

    • Peter Trondsen
      March 22, 2019 at 12:46 am

      Saving the script file?

      • March 22, 2019 at 1:12 am

        yeah, this is what I”m running, /Users/name/Downloads/pycreateuserpkg-master/createuserpkg –name=”admin” –uid=600 –fullname=”admin” –shell=”/bin/bash” –admin –version=1.0 –identifier=”com.name.admin_user” “create_admin_user.pkg” /Users/name/Desktop/admin.pkg
        Must provide exactly one filename!

      • March 22, 2019 at 1:41 pm

        figured this out

      • OmniCHI
        April 15, 2019 at 5:21 pm

        How did you figure it out? What was the solution? Why do people do this? Figure out their problem then tell the whole world they fixed it but withhold the actual solution. smh.

  8. Iesopba
    November 20, 2019 at 8:11 pm

    Is there a way to select on which volume you want the account to be created?
    With the original createpkguser installer you could select an external volumes so an account could be created on a Target booted system

  9. Iesopba
    November 20, 2019 at 8:55 pm

    doh! i just found the change install location button, it’s been a long day

  1. No trackbacks yet.

Leave a comment