mkuser
for macOS
mkuser
makes user accounts for macOS with more options, more validation of inputs, and more verification of the created user account than any other user creation tool, including sysadminctl -addUser
and System Preferences!
mkuser
supports and has been thoroughly tested with macOS 10.13 High Sierra and newer (it likely works on older versions of macOS as well, but that hasn't been tested). The newest version of macOS that mkuser
has been tested with as of writing this is macOS 12 Monterey. Because of how mkuser
is written and the built-in tools it uses to create user accounts, it should support future versions of macOS without any major issues as the fundamentals of user creation have been consistent for years across many versions of macOS. If somehow an issue does occur with a future version of macOS, mkuser
's excessive verifications should detect the issue and output a detailed warning or error message.
Along with abundant options and excessive verifications, mkuser
has detailed help info which explains each available option and what affect it will have on the created user account. This info may be informative beyond just using mkuser
since it's really all about all the different kinds of advanced customizations user accounts can have on macOS.
mkuser
is also focused on precision and accuracy. The user accounts created by mkuser
should be indistinguishable from a user account created by sysadminctl -addUser
or System Preferences. This may not sound like much, but if you read through the source you'll see that there are a variety of subtleties and nuances that took quite a bit of research and effort to match exactly what sysadminctl -addUser
and System Preferences does. Also, mkuser
actually does better in some situations to avoid possible errors or macOS bugs! If you're interested in this, there are many detailed technical notes throughout the source that basically serve as a study in user account creation. No other scripted user account creation that I'm aware of creates user accounts as accurately as mkuser
.
mkuser
is a single function within a single script written in bash
with no 3rd-party dependencies (every command that mkuser
calls is included in macOS). If you want to incorporate mkuser
's functionality into your own bash
(not zsh
) script without requiring another file, you can simply copy-and-paste the whole function into your code. Or, of course, you can call the separate mkuser
script file from any code written in any language or directly on the command line.
Some of the features of mkuser
that are not available in other user creation tools are: create a user immediately or save a user creation package, setup automatic login, skip Setup Assistant on first boot and/or first login, prohibit standard users from changing their own password or picture, and prevent the first user from getting a Secure Token, along with all other normal user creation options you would expect and more.
ℹ️ USAGE NOTES
For long form options (multicharacter options starting with two hyphens), case doesn't matter.
For example, --help
, --HELP
, and --Help
are all equal.
For short form options (single character options starting with one hyphen), case DOES matter.
For example, -h
and -H
are NOT equal.
Short form options can be grouped together or passed individually.
But, only a single option within a group can take a parameter and it must be the last option specified within the group.
For example, -qaAn <ACCOUNT NAME>
is valid but -qanA <ACCOUNT NAME>
is not.
Also, -qan <ACCOUNT NAME> -Af <FULL NAME>
is valid but -qaAnf <ACCOUNT NAME> <FULL NAME>
is not.
An error will be displayed if options with parameters are grouped incorrectly.
Long form options can have their word separating hyphens omitted.
For example, --user-id
, --userid
, and --userID
are all equal (since the case also doesn't matter).
This does NOT mean word separating hyphen placement doesn't matter, all of the word separating hyphens must be correct, or all omitted.
Options and their parameters can be separated by whitespace, equals (=), and can also be combined without using whitespace or equals (=).
For example, --uid <UID>
, --uid=<UID>
, --uid<UID>
, -u <UID>
, -u=<UID>
, and -u<UID>
are all valid.
When combining single character options with their parameter, be careful to not set a parameter whose first letter is also a valid single character option as this would be interpreted as combined single character options.
For example, -nuser
would get interpreted as -n -u=ser
(which would error for multiple reasons) instead of -n=user
since -u
is also a valid option.
In these cases, the options and parameters should be separated instead.
But, something like -u401
will always be safe since -4
is not a valid option.
If ANY options or parameters are invalid, the user or package WILL NOT be created.
Instead, the invalid option errors and errors from other checks will be shown.
When creating a user on the current system (not using the --package
option), you will be prompted for confirmation before the user is created.
To NOT be prompted for confirmation (such as when run within a script), you must specify --do-not-confirm
(-F
) or --suppress-status-messages
(-q
) or --stdin-password
.
👤 PRIMARY OPTIONS
--account-name, --record-name, --short-name, --username, --user, --name, -n
< string >
Must only contain lowercase letters, numbers, hyphen/minus (-), underscore (_), or period (.) characters.
The account name cannot start with a period (.) or hyphen/minus (-).
Must be 244 characters/bytes or less and must contain at least one letter.
The account name must not already be assigned to another user.
If omitted, the full name will be converted into a valid account name by converting it to meet the requirements stated above.244 CHARACTER/BYTE ACCOUNT NAME LENGTH LIMIT NOTES:
The account name is used as the OpenDirectory RecordName, which has a hard 244 byte length limit (and the allowed characters are always 1 byte each).
Attempting to create a user with an account name over 244 characters will fail regardless of if you try to usesysadminctl
,dscl
, ordsimport
.ACCOUNT NAMES STARTING WITH PERIOD (.) NOTES:
System Preferences actually allows account names to start with a period (.), but that causes the account name to not show up indscacheutil -q user
ordscl . -list /Users
even though the user does actually exist.
Also, since users with account names starting with a period (.) are NOT properly detected by macOS, their existence can break next available UID assignment bysysadminctl -addUser
and System Preferences and both could keep incorrectly assigning the UID of the account name starting with a period (.) which fails and results in users created with no UID.
Since allowing account names starting with a period (.) would cause those issues andmkuser
would not be able to verify that the user was properly created, starting with a period (.) is not allowed bymkuser
.
--full-name, --real-name, -f
< string >
The only limitations on the characters allowed in the full name are that it cannot be only whitespace and cannot contain control characters other than tabs (such as line breaks).
See notes below about the non-specific length limit of the full name.
The full name must not already be assigned to another user.
If omitted, the account name will be used as the full name.FULL NAME LENGTH LIMIT NOTES:
While there is no explicit length limit, there is a combined byte length limit of the account name, full name, login shell, and home folder path.
If the combined byte length of these 4 attributes is over 1010 bytes, the full name will not load in the "Log Out" menu item of the "Apple" menu.
While this is not a serious issue, it does indicate a bug or limitation within some part of macOS that we do not want to trigger.
mkuser
will do this math for you and show an error with all of the byte lengths as well as how many bytes need to be removed for these 4 attributes to fit within the combined 1010 byte length limitation.
This 1010 byte length limit should not be hit under normal circumstances, so you will generally not need to worry about hitting this limit.
For a bit more technical information about this issue from my testing, search for 1010 bytes within the source of this script.Even though
mkuser
will not allow it, if the byte length of these 4 combined attributes was over 1010 bytes, the account still logs in and seems to work properly other than not loading the full name in the "Log Out" menu item of the "Apple" menu.
But, if this combined byte length is over 2034 bytes, the account cannot login via login window as well as when using thelogin
orsu
commands.
For a bit more technical information about this issue from my testing, search for 2034 bytes within the source of this script.
--unique-id, --user-id, --uid, -u
< integer >
Must be an integer between -2147483648 and 2147483647 (signed 32-bit range).
The User ID (UniqueID) must not already be assigned to another user.
If omitted, the next User ID available from 501 will be used, unless creating a--role-account
or--service-account
, then starting from 200.
If you're the kind of person that has noticed that UIDs may be represented outside of this range, you may be interested in reading the UIDs CAN BE REPRESENTED IN DIFFERENT FORMS comments in this script.NEGATIVE USER ID NOTES:
Negative User IDs should not be created under normal circumstances.
Negative User IDs are normally reserved for special system users and users with negative User IDs may not behave properly or as expected.
--generated-uid, --guid, --uuid, -G
< string >
Must be 36 characters of only capital letters, numbers, and hyphens/minuses (-) in the following format: EIGHT888-4444-FOUR-4444-TWELVE121212
The Generated UID (GUID) must not already be assigned to another user.
If omitted, a random Generated UID will be assigned by macOS.
You should not normally need to manually specify a Generated UID.
--primary-group-id, --group-id, --group, --gid, -g
< integer >
Must be an integer between -2147483648 and 2147483647 (signed 32-bit range).
The Group ID must already exist, non-existent Group IDs will not be created.
If omitted, the default Primary Group ID of 20 (staff) will be used, unless creating a--service-account
, then -2 (nobody) will be used.
If you're the kind of person that has noticed that GIDs may be represented outside of this range, you may be interested in reading the UIDs CAN BE REPRESENTED IN DIFFERENT FORMS comments in this script.
--login-shell, --user-shell, --shell, -s
< existing path || command name >
The login shell must be the path to an existing executable file, or a valid command name that can be resolved using
which
(searching within "/usr/bin", "/bin", "/usr/sbin", "/sbin", and "/usr/libexec").
You must specify the path if the desired login shell is in another location.
If omitted, "/bin/zsh" will be used on macOS 10.15 Catalina and newer and "/bin/bash" will be used on macOS 10.14 Mojave and older.
🔐 PASSWORD OPTIONS
--password, --pass, -p
< string >
The password must meet the systems password content policy requirements.
The default password content requirements are that it must be at least 4 characters, or a blank/empty password when FileVault IS NOT enabled.If no password content policy is set (such as by default on macOS 10.13 High Sierra), the default requirements will still be enforced by
mkuser
.
Also, only the default password requirements will be enforced when outputting a user creation package, see notes below for more information.Regardless of the password content policy,
mkuser
enforces a maximum password length of 511 bytes, or 251 bytes when enabling auto-login.
See notes below for more details about these maximum length limitations.The only limitation on the characters allowed in the password that
mkuser
enforces is that it cannot contain any control characters such as line breaks or tabs (but a custom password content policy may enforce other limitations).
If omitted, a blank/empty password will be specified.BLANK/EMPTY PASSWORD NOTES:
Blank/empty passwords are not allowed by default when FileVault is enabled.
When FileVault is not enabled, a user with a blank/empty password WILL be able to log in and authenticate GUI prompts, but WILL NOT be able to authenticate "Terminal" commands likesudo
,su
, orlogin
, for example.AUTO-LOGIN 251 BYTE PASSWORD LENGTH LIMIT NOTES:
Auto-login simply does not work with passwords longer than 251 bytes.
I am not sure if this is a bug or an intentional limitation, but if you set a password of 252 bytes or more and enable auto-login, macOS will boot to the login window instead of automatically logged into the user.
I am not sure what exactly is failing internally, but the behavior is as if the encoded auto-login password is incorrect.
I have confirmed this IS NOT an issue with the auto-login password encoding withinmkuser
since the same thing happens when enabling auto-login in the "Users & Groups" pane of the "System Preferences".511 BYTE PASSWORD LENGTH LIMIT NOTES:
Most of macOS can technically support passwords longer than 511 bytes, but both thelogin
andsu
commands fail with passwords over 511 bytes.
Since 512 byte or longer passwords cannot work in all possible situations, they are not allowed sincemkuser
exists to make fully functional users.
If not being able to use thelogin
andsu
commands is not an issue, and you want to use a longer password, you can just set a temporary password when creating a user withmkuser
and then change the password to something 512 bytes or longer manually usingdscl . -passwd
.
If you manually set a password 512 bytes or longer, you will be able to login via login window as well as authenticate graphical prompts, such as unlocking "System Preferences" panes if the user in an admin.
For fun, I tested logging in via login window with passwords up to 10,000 bytes (typed via an Arduino) and unlocking "System Preferences" panes with passwords up to 150,000 bytes (copy-and-pasted).
Longer passwords took overly long for the Arduino to type or macOS to paste.
But, that longer password testing was done with non-Secure Token accounts.
When an account has a Secure Token, there are other limitations described in the SECURE TOKEN ADMIN 1022 BYTE PASSWORD LENGTH LIMIT NOTES in the help information for the--secure-token-admin-password
option below.PASSWORDS IN PACKAGE NOTES:
When outputting a user creation package (with the--package
option), only the default password content requirements are checked since the password content policy may be different on the target system.
The target systems password content policy will be checked when the package is installed and the user will not be created if the password does not meet the target systems password content policy requirements.The specified password (along with the existing Secure Token admin password, if specified) will be securely obfuscated within the package in such a way that the passwords can only be deobfuscated by the specific and unique script generated during package creation and only when run during the package installation process.
For more information about how passwords are securely obfuscated within the package, read the comments within the code of this script starting at: OBFUSCATE PASSWORDS INTO RUN-ONLY APPLESCRIPT
Also, when the passwords are deobfuscated during the package installation, they will NOT be visible in the process list or written to the filesystem since they will only exist as variables within the script and be passed to an internalmkuser
function.
--stdin-password, --stdin-pass, --sp
< no parameter (stdin) >
Include this option with no parameter to pass the password via "stdin" using a pipe (
|
) or here-string (<<<
), etc.
Although, it is recommended to use a pipe instead of a here-string because a pipe is more secure since a here-string creates a temporary file which contains the specified password while a pipe does not.
If you haven't used anecho
and pipe (|
) before, it looks like this:echo '<PASS>' | mkuser [OPTIONS] --stdin-password [OPTIONS]
Passing the password via "stdin" instead of directly with the--password
option hides the password from the process list.
Sinceecho
is a builtin inbash
andzsh
and not an external binary command, theecho
command containing the password as an argument is also never visible in the process list.
The help information for the--password
option above also applies to passwords passed via "stdin".
NOTICE: Specifying--stdin-password
also ENABLES--do-not-confirm
since accepting "stdin" disrupts the ability to use other command line inputs.
--password-prompt, --pass-prompt, --pp
< no parameter >
Include this option with no parameter to be prompted for the new user password on the command line before creating the user or package.
This option allows you to specify a password without it being saved in your command line history as well as hides the password from the process list.
The help information for the--password
option above also applies to passwords entered via command line prompt.
--no-password, --no-pass, --np
< no parameter >
Include this option with no parameter to set no password at all instead of a blank/empty password (like when the
--password
option is omitted).
This option is equivalent to setting the password to "*" with--password '*'
and is here as a separate option for convenience and information.
Setting the password to "*" is a special character that indicates to macOS that this user does not have any meaningful password set.
When a user has the "*" password set, it cannot login by any means and it will also not get any AuthenticationAuthority set in the user record.
When the "*" password is set AND no AuthenticationAuthority exists, the user will not show in the users list in "Users & Groups" pane of the "System Preferences" and will also not show up in the login window.
If you choose to start a user out with no password for some reason, you can always set their password later withdscl . -passwd
.If you include the
--prevent-secure-token-on-big-sur-and-newer
option with this option, that would create an AuthenticationAuthority attribute with the special tag to prevent a Secure Token from being granted.
Since that user would no longer have BOTH no AuthenticationAuthority AND the "*" password, they would show in the users list in "Users & Groups" pane of the "System Preferences" as well as the login window list of users, but could not log in since no meaningful password is set.
--password-hint, --hint, --ph
< string >
Must be 280 characters or less and the only limitations on the characters allowed in the password hint are that it cannot be only whitespace and can't contain control characters other than line breaks (\n) or tabs (\t).
If omitted, no password hint will be set.280 CHARACTER PASSWORD HINT LENGTH LIMIT NOTES:
The password hint popover in the non-FileVault login window will only display up to 7 lines at about 40 characters per line.
This results in 280 characters being a reasonable maximum length.
Since each character is a different width, 40 characters per line is just an estimation and less or more may fit depending on the characters, for example, only 14 smiley face emoji fit on a single line.
If line breaks are included, they are rendered in the password hint popover and that can make less characters show since only up to 7 lines will show.
If for some reason you need or want a longer password hint, you can just set a temporary password hint when creating a user withmkuser
and then change the password hint to something longer manually with:dscl . -create /Users/<ACCOUNT NAME> AuthenticationHint '<PASSWORD HINT>'
--prohibit-user-password-changes
< no parameter >
Include this option with no parameter to prohibit the user from being able to change their own password without administrator authentication.
The password can still be changed in the "Users & Groups" pane of the "System Preferences" when unlocked and authenticated by an administrator.
📁 HOME FOLDER OPTIONS
--home-folder, --home-path, --home, -H
< non-existing path >
The home folder path must not currently exist and must be directly within "/Users/" or "/private/var/" (or "/var/"), or on an external drive (but that is not recommended).
The special "/var/empty" and "/dev/null" paths are also allowed.
The total length of the home folder path must be 511 bytes or less, or home folder creation will fail during login orcreatehomedir
.
Each folder within the home folder path must be 255 bytes or less each, as that is the max folder/file name length set by macOS.
If the home folder is not within the "/Users/" folder, the users Public folder will not be shared.
If omitted, the home folder will be set to "/Users/<ACCOUNT NAME>".
--do-not-share-public-folder, --dont-share-public
< no parameter >
Include this option with no parameter to NOT share the users Public folder.
The users Public folder will be shared by default unless the users home folder is hidden or is not within the "/Users/" folder.
The users Public folder can still be shared manually in the "File Sharing" section of the "Sharing" pane of the "System Preferences".
--do-not-create-home-folder, --dont-create-home
< no parameter >
Include this option with no parameter to NOT create the users home folder.
The users home folder will be created by macOS when the user is logged in graphically via login window, but will not be created when logging in via "Terminal" using thelogin
orsu
commands, for example.
To create the home folder at anytime via "Terminal" or script, you can use thecreatehomedir -cu <ACCOUNT NAME>
command.
When using this option, you CANNOT also specify--hide homeOnly
or--skip-setup-assistant firstLoginOnly
since they require the home folder.
🖼 PICTURE OPTIONS
--picture, --photo, --pic, -P
< existing path || default picture filename >
Must be a path to an existing image file that is 1 MB or under, or be the filename of one of the default user pictures located within the "/Library/User Pictures/" folder (with or without the file extension, such as "Earth" or "Penguin.tif").
When outputting a user creation package (with the--package
option), the specified picture file will be included in the user creation package.
If omitted, a random default user picture will be assigned.
--no-picture, --no-photo, --no-pic
< no parameter >
Include this option with no parameter to not set any picture instead of a random default user picture (like when the
--picture
option is omitted).
When no picture is set, a grey head and shoulders silhouette icon is used.
--prohibit-user-picture-changes
< no parameter >
Include this option with no parameter to prohibit the user from being able to change their own picture without administrator authentication.
The picture can still be changed in the "Users & Groups" pane of the "System Preferences" when unlocked and authenticated by an administrator.
🎛 ACCOUNT TYPE OPTIONS
--administrator, --admin, -a
< no parameter >
Include this option with no parameter to make the user an administrator.
Administrators can manage other users, install apps, and change settings.If omitted, a standard user will be created.
Standard users can install apps and change their own settings, but can't add other users or change other users' settings.For more information about administrator and standard account types, visit: https://support.apple.com/guide/mac-help/mtusr001
--hidden, --hide
< userOnly || homeOnly || both (or no parameter) >
Include this option with either no parameter or specify "both" to hide both the user and their home folder.
Specify "userOnly" to hide only the user and keep the home folder visible.
Hidden users will not show in the users list in "Users & Groups" pane of the "System Preferences" unless they are currently logged in, and will also not show up in the login window list of users (unless they have a Secure Token and FileVault is enabled).
A hidden user can still be logged into by using text input fields in the non-FileVault login window.Specify "homeOnly" to hide only the home folder and keep the user visible.
If the home folder is hidden, the users Public folder will not be shared.Any other parameters are invalid and will cause the user to not be created.
--sharing-only-account, --sharing-account, --sharing-only, --sharing, --soa
< no parameter >
Include this option with no parameter to create a "Sharing Only" account.
This is identical to a "Sharing Only" account that can be created in the "Users & Groups" pane of the "System Preferences" when adding a new user and changing the "New Account" pop-up menu to "Sharing Only".
A "Sharing Only" account can access shared files remotely, but can't log in or change settings on the computer.A "Sharing Only" account is equivalent to creating a user with the login shell set to "/usr/bin/false" and home set to "/dev/null" .
This can also be done manually with--shell /usr/bin/false --home /dev/null
, or--no-login --home /dev/null
(see--no-login
help for more information).
Make sure to specify a password when creating a "Sharing Only" account, or it will have a blank/empty password.Also, when running on macOS 11 Big Sur and newer, "Sharing Only" accounts get a special tag added to the AuthenticationAuthority attribute of the user record to let macOS know not to grant a Secure Token.
See--prevent-secure-token-on-big-sur-and-newer
help for more information about preventing macOS from granting an account the first Secure Token.This is here as a separate option for convenience and information.
When using this option, you CANNOT also specify--administrator
, since "Sharing Only" accounts should not be administrators.
Also, you cannot specify--role-account
or--service-account
with this option since they are mutually exclusive account types.
For more information about "Sharing Only" accounts, visit: https://support.apple.com/guide/mac-help/mchlp15577
--role-account, --role, -r
< no parameter >
Include this option with no parameter to create a "Role Account".
A
-roleAccount
option was added tosysadminctl -addUser
in macOS 11 Big Sur, but sadly there is not really any documentation from Apple about what exactly a "Role Account" is or when and why you would want to use one.
I believe you would want to use a "Role Account" when you want a user exclusively to be the owner of files and/or processes and have a password.
Allsysadminctl
states about them is the following: Role accounts require name starting with _ and UID in 200-400 range.
Andmkuser
has these same requirements to create a "Role Account".
Even though the-roleAccount
option was only added tosysadminctl -addUser
in macOS 11 Big Sur,mkuser
can make "Role Accounts" with the same attributes on older versions of macOS as well.Using this option is the same as creating a "Role Account" using
sysadminctl -addUser
with a command like:sysadminctl -addUser _role -UID 201 -roleAccount
This examplesysadminctl -addUser
command would create a "Role Account" with the account name and full name of "_role" and the User ID "201".
IMPORTANT: The example account would be created with a blank/empty password.If you want to make an account exclusively to be the owner of files and/or processes that has NO password, you probably want to use the
--service-account
option instead of this--role-account
option.Through investigation of a "Role Account" created by
sysadminctl -addUser
, a "Role Account" is equivalent to creating a hidden user with account name starting with "_" and login shell "/usr/bin/false" and home "/var/empty".
The previous example account could be created manually withmkuser
using:-n _role -u 201 -s /usr/bin/false -H /var/empty --hide userOnly
or--name _role --uid 201 --no-login --home /var/empty --hide userOnly
.
See--no-login
help for more information about login shell "/usr/bin/false".
See--hidden
help for more information about hiding users (--hide userOnly
).This is here as a separate option for convenience and information.
So, this same example account could be created withmkuser
using:--account-name _role --uid 201 --role-account
Unlike
sysadminctl -addUser
which requires the User ID to be specified manually,mkuser
can assign the next available User ID starting from 200.
So if the User ID is not important, you can just use--name _role --role
to make this same example account with the next User ID in the 200-400 range.
sysadminctl -addUser
does not allow creating an admin "Role Account".
If you runsysadminctl -addUser _role -UID 201 -roleAccount -admin
, the-admin
option is silently ignored bysysadminctl -addUser
.
mkuser
also does not allow a "Role Account" to be an admin, but errors when using the--admin
option with--role-account
instead of ignoring it.
Also, you cannot specify--sharing-only
or--service-account
with this option since they are mutually exclusive account types.
--service-account, --service, --sa
< no parameter >
Include this option with no parameter to create a "Service Account".
A "Service Account" is similar to a "Role Account" in that it exists exclusively to be the owner of files and/or processes but has NO password.
This is like macOS built-in accounts, such as the "FTP Daemon" (_ftp) user.Through investigation of the built-in macOS "Service Accounts", a "Service Account" is roughly equivalent to creating a standard user with name starting with "_", login shell "/usr/bin/false", home "/var/empty", and NO password (see
--no-password
for more information about that).
See--no-login
help for more information about login shell "/usr/bin/false".
But, this is just a basic template of a "Service Accounts".These are not all hard requirements for a "Service Account".
The hard requirements are that the account name must start with "_", must have NO password, must have no picture, CANNOT be an admin, and the home folder cannot be within the "/Users/" folder.
But, you can specify any User ID, Primary Group ID, or login shell.
If--user-id
is omitted, the next available User ID starting from 200 will be assigned by default (the same as a "Role Account").
If--group-id
is omitted, the -2 (nobody) group will be used.
If--login-shell
is omitted, "/usr/bin/false" will be used.
If--home-folder
is omitted, "/var/empty" will be used.Also, you cannot specify
--sharing-only
or--role-account
with this option since they are mutually exclusive account types.While you can pretty much make a "Service Account" manually using the other
mkuser
options, there is a difference when you specify--service-account
.
All other account types get a variety of attributes added to the user record that allow the user to manage some aspects of their own account, but none of these attributes are included for built-in macOS "Service Accounts".
To match the built-in macOS "Service Accounts", these management attributes will not be included in the user record when specifying--service-account
.
Excluding some (not all) of these specific management attributes is how the--prohibit-user-password-changes
and--prohibit-user-picture-changes
options work.GROUPS SPECIFICALLY FOR SERVICE ACCOUNTS NOTES:
Many built-in macOS "Service Accounts" have a group specifically for them, and often that Group ID is the same as the "Service Accounts" User ID and the Group ID is set to the Primary Group ID of the "Service Account".If you specify a Primary Group ID (
--group-id
), it must already exist.
If you want to create a group just to be used with a "Service Account", you can do that easily before making the "Service Account" with:dseditgroup -o create -i <GROUP ID> -r <GROUP FULL NAME> <GROUP NAME>
When you do this before creating a "Service Account" withmkuser
, you can set the "Service Account" Primary Group ID to this Group ID with--gid
.
After creating the "Service Account", you can also add it to the group with:dseditgroup -o edit -a <SERVICE ACCOUNT NAME> -t user <GROUP NAME>
But, that is not really necessary if the "Service Account" already has its Primary Group ID set to the Group ID.
--prevent-secure-token-on-big-sur-and-newer, --prevent-secure-token, --no-st
< no parameter >
Include this option with no parameter to prevent the user from being automatically granted the first Secure Token on macOS 11 Big Sur and newer when and if they are being created when the first Secure Token has not yet been automatically granted by macOS.
This option is helpful when creating scripted users before going through Setup Assistant that you do not want to be granted the first Secure Token, which would prevent the Setup Assistant user from getting a Secure Token.
This option will add a special tag to the AuthenticationAuthority attribute of the user record to let macOS know not to grant a Secure Token.
For more information about this Secure Token prevention tag, visit: https://support.apple.com/guide/deployment/dep24dbdcf9e
A Secure Token could still be manually granted to this user after specifying this option on macOS 11 Big Sur and newer withsysadminctl -secureTokenOn
, or by an MDM Bootstrap Token when logging in graphically via login window.
This option has no effect on macOS 10.15 Catalina and older, but there is useful information below about first Secure Token behavior all the way back to macOS 10.13 High Sierra when Secure Tokens were first introduced.VOLUME OWNER ON APPLE SILICON NOTES:
On Apple Silicon Macs, users that do not have a Secure Token cannot be Volume Owners, which means they will not be able to approve system updates (among other things).
For more information about Volume Ownership on Apple Silicon, visit the Apple Platform Deployment link above.macOS 11 Big Sur AND NEWER FIRST SECURE TOKEN NOTES:
On macOS 11 Big Sur and newer, the first Secure Token is granted to the first administrator or standard user when their password is set, regardless of their UID.
This essentially means the first Secure Token is granted right when the first user is created.
This is different from previous versions of macOS which would grant the first Secure Token upon first login or authentication.
Since this behavior is more aggressive than previous first Secure Token behavior, a new way has been added to selectively prevent a user from being granted the first Secure Token.
This is done by adding a special tag to the AuthenticationAuthority attribute in the user record before the users password has been set.
Whilemkuser
includes this option and takes care of the necessary timing, it's worth noting that when creating users withsysadminctl -addUser
it's actually impossible to prevent a Secure Token in this way since the password is always set during that user creation process, even if it's just a blank/empty password.
When users are created with this tag in their AuthenticationAuthority, the first user that does not have this special tag will get the first Secure Token when their password is set (basically, upon creation).
An exception to this behavior is when utilizing MDM along with the MDM-created Managed Administrator, which will not be granted the first Secure Token unless it is the first to login or authenticate (similar to the macOS 10.15 Catalina behavior described below) because this user is created with their password pre-hashed and placed directly into their user record rather than the password being set by "normal" methods (if you're familiar withpycreateuserpkg
, it also pre-hashes the passwords resulting in the users it creates also not being granted the first Secure Token unless they are the first to login or authenticate).
In general, you will want to make sure the the first user being granted a Secure Token is also an administrator so that they are allowed to do all possible operations on macOS (especially on T2 and Apple Silicon Macs).macOS 10.15 Catalina FIRST SECURE TOKEN NOTES:
On macOS 10.15 Catalina, the first Secure Token is granted to the first administrator (not standard user) to login or authenticate, regardless of their UID.
Even thoughmkuser
will always verify the password (using nativeOpenDirectory
methods) during the user creation process (which is an authentication that could trigger granting the first Secure Token), this authentication happens before the user is added to the "admin" group (if they are configured to be an administrator).
This means that users will never be an administrator during this authentication within themkuser
process and therefore will not be granted the first Secure Token at that moment.
The first Secure Token will then be granted by macOS to the first administrator to login or authenticate aftermkuser
has finished.
This is the same first Secure Token behavior that can be expected from any other user creation method that I'm aware of.
If for some reason you want to immediately grant an administrator created bymkuser
the first Secure Token, you can manually rundscl . -authonly
aftermkuser
has finished.macOS 10.14 Mojave AND macOS 10.13 High Sierra FIRST SECURE TOKEN NOTES:
The following information only applies to macOS on an APFS volume (and not HFS+) as Secure Tokens are exclusively an APFS feature.
The Secure Token behavior is slightly different on macOS 10.14 Mojave and macOS 10.13 High Sierra than it is on new versions of macOS.
Also,mkuser
's process has an effect on the default macOS behavior of granting the first Secure Token.
Basically, the first Secure Token is granted to the first administrator or standard user to login or authenticate which has a UID of 500 or greater if and only if they are the only user with a UID of 500 or greater.
This means that if multiple users with UIDs of 500 or greater were to be created before any of them logged in or authenticated, no first Secure Token would be granted automatically by macOS (which is not a great situation to get into by accident).
But,mkuser
simplifies this complexity since the password will always be verified during the user creation process (using nativeOpenDirectory
methods), which means the users first authentication actually happens during themkuser
user creation process.
Therefore, when usingmkuser
, the first Secure Token will always be granted to the first user created with a UID of 500 or greater when their password is verified during themkuser
process.
If you do not want the first user you are creating withmkuser
to be granted the first Secure Token, such as for a management account, simply set their UID below 500 and macOS will not grant them the first Secure Token when their password is verified bymkuser
.
Then, the first user created bymkuser
with a UID of 500 or greater or the first user created by going through first boot Setup Assistant will get the first Secure Token as intended.
You can also simply adjust the order of users created to be sure the user with a UID of 500 or greater that you want to be granted the first Secure Token is created first.
In general, you will want to make sure the first user being granted a Secure Token is also an administrator so that they are allowed to do all possible operations on macOS, such as grant other users a Secure Token.ALL VERSIONS OF macOS SECURE TOKEN NOTES:
Once the first Secure Token has been granted, any subsequent users created bymkuser
or by going through first boot Setup Assistant will not automatically be granted a Secure Token by macOS since the first Secure Token has already been granted.
If you're usingmkuser
to create users before going through Setup Assistant, and you want the user created by first boot Setup Assistant to be granted the first Secure Token, be sure to take the necessary steps for each version of macOS (as outline above) to ensure any users created bymkuser
are not granted the first Secure Token.
Once the first Secure Token has been granted by macOS, you must usesysadminctl -secureTokenOn
to grant other users a Secure Token and authenticate the command with an existing Secure Token administrator either interactively or by passing their credentials with the-adminUser
and-adminPassword
options.
Or,mkuser
can securely take care of this for you when creating new users if you pass an existing Secure Token admins credentials using the--secure-token-admin-account-name
option along with one of the three different Secure Token admin password options below.
See the SECURE TOKEN ADMIN 1022 BYTE PASSWORD LENGTH LIMIT NOTES in the help information for the--secure-token-admin-password
option below and the PASSWORDS IN PACKAGE NOTES in help information for the--password
option above for more information about how passwords are handled securely bymkuser
, all of which also apply to Secure Token admin passwords.
Users created in the "Users & Groups" pane of the "System Preferences" will only get a Secure Token when the pane has been unlocked by an existing Secure Token administrator.
Similarly, users created usingsysadminctl -addUser
will only get a Secure Token when the command is authenticated with an existing Secure Token administrator (the same way as when using thesysadminctl -secureTokenOn
option).
The only exception to this subsequent Secure Token behavior is when utilizing MDM with a Bootstrap Token.BOOTSTRAP TOKEN NOTES (MDM-ENROLLED macOS 10.15 Catalina AND NEWER ONLY):
The Apple Platform Deployment link above also explains the Bootstrap Token.
But, some useful details are included below as well as information about howmkuser
can simplify the creation of the Bootstrap Token on macOS 11 Big Sur and newer when the system is enrolled in a supported MDM.For a Bootstrap Token to be able to be created, the MDM must support it.
The Bootstrap Token was first introduced in macOS 10.15 Catalina, but required Automated Device Enrollment (ADE/DEP) and was limited to granting Secure Tokens to mobile accounts logging in graphically via login window (but not when using thelogin
orsu
commands) as well as the optional MDM-created Managed Administrator.
Starting in macOS 11 Big Sur, the Bootstrap Token functionality was expanded to support all User Approved MDM Enrollment (UAMDM) methods and also to grant Secure Tokens to local users logging in graphically.
Also, more functionality was added for Apple Silicon in macOS 11 Big Sur.
On Apple Silicon, the Bootstrap Token can be used to authorize installation of both kernel extensions and software updates when managed using MDM.
Starting in macOS 12 Monterey, the Bootstrap Token can also be used to silently authorize an Erase All Content and Settings command for Apple Silicon Macs (not required for T2 Macs) when triggered through MDM.
One way to think of the Bootstrap Token is that it is like an invisible Secure Token/Volume Owner administrator account that can be used to automate actions via MDM that normally require authentication by a regular Secure Token/Volume Owner administrator account.Under normal circumstances, the first user would be created manually during Setup Assistant and then be granted the first Secure Token.
The Bootstrap Token would also be created during that process as that user is automatically logged in graphically.While it is generally recommended that the first administrator be created manually by the end user during Setup Assistant (since macOS will grant them the first Secure Token and then create the Bootstrap Token), if you choose to have
mkuser
create the first Secure Token user before that point, or choose to skip manual user creation during Setup Assistant, then a Secure Token user would need to manually log in graphically for the Bootstrap Token to be created.
On macOS 11 Big Sur and newer,mkuser
simplifies this whenmkuser
is used to create the first Secure Token administrator by running theprofiles install -type bootstraptoken
command and securely authorizing it with the credentials of the newly created user during themkuser
process.
mkuser
will only do this on macOS 11 Big Sur and newer because the first Secure Token will be granted by macOS when the password is set during themkuser
process (see macOS 11 Big Sur AND NEWER FIRST SECURE TOKEN NOTES above for more information).
On macOS 10.15 Catalina, the first Secure Token will NOT be granted by macOS during themkuser
process (see macOS 10.15 Catalina FIRST SECURE TOKEN NOTES above for more information) and thereforemkuser
will not be able to create and escrow the Bootstrap Token.On macOS 10.15.4 Catalina and newer, when a Secure Token enabled user logs in graphically for the first time, the Bootstrap Token is created and escrowed to the supported MDM when internet is available (on older versions of macOS 10.15 Catalina, the Bootstrap Token was only created and escrowed automatically during the Setup Assistant user creation process).
This would normally be when the first administrator logs in graphically and is granted the first Secure Token by macOS which will also create and escrow the Bootstrap Token during that same graphical login process.
If internet is not available during any Bootstrap Token creation event, the Bootstrap Token will be created but will NOT be escrowed to MDM and will therefore not be able to grant other users a Secure Token until it has been escrowed to MDM.
If this happens, the Bootstrap Token will be escrowed to MDM the next time that user logs in graphically when internet is available.
Also, the Bootstrap Token can be manually created and/or escrowed to the supported MDM using theprofiles install -type bootstraptoken
command.For
mkuser
to create and escrow the Bootstrap Token on macOS 11 Big Sur and newer, the account name and password must be passed to theprofiles install -type bootstraptoken
command.
To do this in the most secure way possible (so that the password is never visible in the process list or written to the filesystem), the password is NOT passed directly as an argument but is instead passed using the interactive command line prompt (viaexpect
automation).
But, theprofiles install -type bootstraptoken
command line password prompt fails to accept passwords over 128 bytes even if the password is correct.
Usingexpect
to pass the password securely has one other limitation, which is that it does not support emoji characters.
If the password is over 128 bytes or contains emoji (even though both are quite rare), then the Bootstrap Token creation will fail with a warning.
Longer passwords (up to 512 bytes) as well as passwords containing emoji can be passed toprofiles install -type bootstraptoken
directly using the-user
and-password
arguments, but that would make the password visible in the process list.
Sincemkuser
strives to handle passwords in the most secure ways possible, only the secure command line prompt method usingexpect
will be attempted, and if it fails then the user will need to be logged in graphically to create and escrow the Bootstrap Token, or the insecureprofiles install -type bootstraptoken -user <USER> -password <PASSWORD>
command will need to be run manually after themkuser
process is done.
Also, if the first Secure Token user is created with a blank/empty password, they cannot authenticate theprofiles install -type bootstraptoken
command and a Bootstrap Token will also NOT be created when logged in graphically.
The Secure Token user having some password set is simply a requirement to be able to create the Bootstrap Token.Once the Bootstrap Token has been created and escrowed, it will only grant Secure Tokens to users logging in graphically via login window (but not when using the
login
orsu
commands) and internet must be available during the macOS login process to communicate with the MDM.
Except if a user has a blank/empty password, then the Bootstrap Token will not grant that user a Secure Token.
Otherwise, there is NO WAY to prevent the Bootstrap Token from granting an account a Secure Token when logging in graphically, not even when this--prevent-secure-token-on-big-sur-and-newer
option is specified as that only applies to macOS granting the first Secure Token, not to subsequent Secure Tokens granted by the Bootstrap Token.
--secure-token-admin-account-name, --st-admin-name, --st-admin-user, --st-name
< string >
Specify an existing Secure Token administrator account name (not full name) along with their password (using one of the three different options below) to be used to grant the new user a Secure Token.
This option is ignored on HFS+ volumes since Secure Tokens are APFS-only.
--secure-token-admin-password, --st-admin-pass, --st-pass
< string >
The password will be validated to be correct for the specified
--secure-token-admin-account-name
.
The password must be 1022 bytes or less (see notes below for more info).
If omitted, blank/empty password will be specified.
This option is ignored on HFS+ volumes since Secure Tokens are APFS-only.See PASSWORDS IN PACKAGE NOTES in help information for the
--password
option above for more information about how the Secure Token admin password is securely obfuscated within a package.SECURE TOKEN ADMIN 1022 BYTE PASSWORD LENGTH LIMIT NOTES:
To grant the new user a Secure Token, the user and existing Secure Token admin passwords must be passed tosysadminctl -secureTokenOn
.
To do this in the most secure way possible (so that they are never visible in the process list or written to the filesystem), the passwords are NOT passed directly as arguments but are instead passed via "stdin" using the command line prompt options.
But, this technique fails with Secure Token admin passwords over 1022 bytes.
For a bit more technical information about this limitation from my testing, search for 1022 bytes within the source of this script.
The length of the new user password is not an issue for this command since it is limited to a maximum of 511 bytes as described in the 511 BYTE PASSWORD LENGTH LIMIT NOTES in help information for the--password
option above.
Sincemkuser
strives to handle passwords in the most secure ways possible, the password length of Secure Token admin is limited to 1022 bytes so that the password can be passed tosysadminctl -secureTokenOn
in a secure way that never makes it visible in the process list or writes it to the filesystem.
If your existing Secure Token admin has a longer password for any reason, you can use it to manually grant a Secure Token after creating a non-Secure Token account withmkuser
by insecurely passing the password directly tosysadminctl -secureTokenOn
as an argument since longer passwords are properly accepted when passed that way.
--fd-secure-token-admin-password, --fd-st-admin-pass, --fd-st-pass
< file descriptor path (via process substitution) >
The file descriptor path must be specified via process substitution.
The process substitution command mustecho
the Secure Token admin password.
If you haven't used process substitution before, it looks like this:mkuser [OPTIONS] --fd-secure-token-admin-password <(echo '<PASS>') [OPTIONS]
Passing the password via process substitution instead of directly with the--secure-token-admin-password
option hides the password from the process list and does not create any temporary file containing the password.
Sinceecho
is a builtin inbash
andzsh
and not an external binary command, theecho
command containing the password as an argument is also never visible in the process list.
The help information for the--secure-token-admin-password
option above also applies to Secure Token admin passwords passed via process substitution.
This option is ignored on HFS+ volumes since Secure Tokens are APFS-only.
--secure-token-admin-password-prompt, --st-admin-pass-prompt, --st-pass-prompt
< no parameter >
Include this option with no parameter to be prompted for the Secure Token admin password on the command line before creating the user or package.
This option allows you to specify a Secure Token admin password without it being saved in your command line history as well as hides the password from the process list.
The help information for the--secure-token-admin-password
option above also applies to Secure Token admin passwords entered via command line prompt.
This option is ignored on HFS+ volumes since Secure Tokens are APFS-only.
NOTICE: This option cannot be used when--stdin-password
is specified since accepting "stdin" disrupts the ability to use other command line inputs.
🚪 LOGIN OPTIONS
--automatic-login, --auto-login, -A
< no parameter >
Include this option with no parameter to set automatic login for the user.
Enabling automatic login stores the users password in the filesystem in an obfuscated but insecure way.
If automatic login is already setup for another user, it'll be overwritten.
If FileVault is enabled, automatic login is not possible or allowed and this option will be ignored (and a warning will be displayed).
--prevent-login, --no-login, --nl
< no parameter >
Include this option with no parameter to prevent this user from logging in.
This option is equivalent to setting the login shell to "/usr/bin/false" which can also be done directly with--login-shell /usr/bin/false
.
This is here as a separate option for convenience and information.
When the login shell is set to "/usr/bin/false", the user is will not show in the "Users & Groups" pane of the "System Preferences" and will also not show up in the non-FileVault login window list of users.If FileVault is enabled and one of these users has a password and is granted a Secure Token, they WILL show in the FileVault login window and can decrypt the volume, but then the non-FileVault login will be hit to fully login to macOS with another user account.
Unlike hidden users, these user CANNOT be logged into using text input fields in the non-FileVault login window.Even if one of these users has a password set, they CANNOT authenticate "Terminal" commands like
su
, orlogin
as well as NOT being able to log in remotely viassh
.
They also CANNOT authenticate graphical prompts, such as unlocking "System Preferences" panes if they are an administrator.
But, if these users are an admin, they CAN run AppleScriptdo shell script
commandswith administrator privileges
.
--skip-setup-assistant, --skip-setup, -S
< firstBootOnly || firstLoginOnly || both (or no parameter) >
Include this option with either no parameter or specify "both" to skip both the first boot and first login Setup Assistant screens.
Specify "firstBootOnly" to skip only the first boot Setup Assistant screens.
This affects all users and has no effect if first boot Setup Assistant has already been completed.
If Setup Assistant is already running when the user is being created,mkuser
will exit Setup Assistant after the user creation process is done.Specify "firstLoginOnly" to skip only the users first login Setup Assistant screens.
This affects only this user and will also skip any and all future user Setup Assistant screens that may appear when and if macOS is updated.Any other parameters are invalid and will cause the user to not be created.
📦 PACKAGING OPTIONS
--package-path, --pkg-path, --package, --pkg
< folder path || pkg file path || no parameter (working directory) >
Save distribution package to create a user with the other specified options.
This will not create a user immediately on the current system, but will save a distribution package file that can be used on another system.
The distribution package (product archive) created will be suitable for use withstartosinstall --installpackage
orinstaller -pkg
or "Installer" app, and is also "no payload" which only runs scripts and leaves no receipt.
If no path is specified, the current working directory will be used along with the default filename: <PKG ID>-<PKG VERSION>.pkg
If a folder path is specified, the default filename will be used within the specified folder.
If a full file path ending in ".pkg" is specified, that whole path and filename will be used.
For any of these path options, if the exact filename already exists in the specified folder, it will be OVERWRITTEN by a newly created package.
--package-identifier, --pkg-identifier, --package-id, --pkg-id
< string >
Specify the bundle identifier string to use for the package (only valid when using the
--package
option).
Must be 248 characters/bytes or less and start with a letter or number and can only contain alphanumeric, hyphen/minus (-), underscore (_), or dot (.) characters.
If the package identifier is over 248 characters, the installation would fail to extract the package scripts since they are extracted into a folder named with the package identifier and appended with a period plus 6 random characters which would make that folder name over the macOS 255 byte max.
If omitted, the default identifier will be used: mkuser.pkg.<ACCOUNT NAME>
--package-version, --pkg-version, --pkg-v
< version string >
Specify the version string to use for the package (only valid when using the
--package
option).
Must start with a number or letter and can only contain alphanumeric, hyphen/minus (-), or dot (.) characters.
If omitted, the current date will be used in the format: YYYY.M.D
--package-signing-identity, --package-sign, --pkg-sign
< string >
Specify the installer package signing identity string to use for the package (only valid when using the
--package
option).
The string must be for an existing installer package signing identity in the Keychain, and in the proper format: Developer ID Installer: Name (Team ID)
If omitted, the package will not be signed.
⚙️ MKUSER OPTIONS
--do-not-confirm, --no-confirm, --force, -F
< no parameter >
By default,
mkuser
prompts for confirmation on the command line before creating a user on the current system.
Include this option with no parameter to NOT prompt for confirmation.
This option is ignored when outputting a user creation package (with the--package
option) since no user will be created on the current system.
NOTICE: Specifying--suppress-status-messages
OR--stdin-password
also ENABLES--do-not-confirm
.
--suppress-status-messages, --quiet, -q
< no parameter >
Include this option with no parameter to not output any status messages that would be sent to "stdout".
Any errors and warning that are sent to "stderr" will still be outputted.
NOTICE: Specifying--suppress-status-messages
also ENABLES--do-not-confirm
.
--check-only, --dry-run, --check, -c
< no parameter >
Include this option with no parameter to check if the other specified options are valid and output the settings a user would be created with.
This option is ignored when outputting a user creation package (with the--package
option) since checking against the current system isn't useful when installing packages on other systems.
--version, -v
< online (or o) || no parameter >
Include this option with no parameter to display the
mkuser
version, and also check for updates when connected to the internet and display the newest version if an update is available.Specify "online" (or "o") to also open the
mkuser
Releases page on GitHub in the default web browser to be able to quickly and easily view the latest release notes as well as download the latest version.This option overrides all other options (including
--help
).
--help, -h
< brief (or b) || online (or o) || no parameter >
Include this option with no parameter to display this help information in Terminal.
Specify "brief" (or "b") to only show options without their descriptions. This can be helpful for quick reference to check option or parameter names.
Specify "online" (or "o") to instead open this README section of the
mkuser
GitHub page in the default web browser to be able quickly and easily view this help information from here.This option overrides all other options (except
--version
).