Skip to content

Instantly share code, notes, and snippets.

@melmatsuoka
Created December 19, 2025 03:27
Show Gist options
  • Select an option

  • Save melmatsuoka/a52c226ad043d9ded403e01bb1aa1097 to your computer and use it in GitHub Desktop.

Select an option

Save melmatsuoka/a52c226ad043d9ded403e01bb1aa1097 to your computer and use it in GitHub Desktop.
Adds a "Sharing only" macOS user account, and properly enables the account for SMB mounts
#!/bin/bash
set -euo pipefail
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root"
exit 1
fi
gid="20" # Default "Staff" group
# Prompt for username
while true; do
read -r -p "Enter short username (no spaces, lowercase only): " username
# Validate username
if [[ ! "${username}" =~ ^[a-z_][a-z0-9_]{0,30}$ ]]; then
echo "Error: Invalid username."
echo "Allowed: lowercase letters, numbers, underscore. Must not start with a number."
continue
fi
# Check if user already exists
if dscl . -read /Users/"${username}" &>/dev/null; then
echo "Error: User \"${username}\" already exists."
continue
fi
break
done
# Prompt for real (display) name
read -r -p "Enter full display name: " realname
# Prompt for password (hidden input)
while true; do
read -r -s -p "Enter password: " password
echo
read -r -s -p "Confirm password: " password_confirm
echo
if [ "${password}" != "${password_confirm}" ]; then
echo "Error: Passwords do not match. Try again."
continue
fi
if [ -z "${password}" ]; then
echo "Error: Password cannot be empty."
continue
fi
break
done
createUser() {
# Create a new user with the username Sharing user
dscl . create /Users/${username}
# Give the display name of the user
dscl . create /Users/${username} RealName "${realname}"
# (Optional) Add a password hint
# dscl . create /Users/${username} hint "Password hint goes here"
# (Optional) Set a profile picture.
# dscl . create /Users/${username} picture "/Provide path to the image.png"
# password for the user account.
dscl . passwd /Users/${username} "${password}"
# Set the unique ID for the user. Provide an ID which is not already used.
dscl . create /Users/${username} UniqueID ${next_uid}
# Set the Group ID for the user
dscl . create /Users/${username} PrimaryGroupID ${gid}
# Deny shell access for the user
# NOTE: This has to be set to /sbin/nologin (not /usr/bin/false) if the account needs SMB access
dscl . create /Users/${username} UserShell /sbin/nologin
# No home directory for the user
dscl . create /Users/${username} NFSHomeDirectory /dev/null
}
enableSMB() {
echo "Enabling SMB access for user \"${username}\"…"
# For more info see: https://stackoverflow.com/questions/7824946/how-to-enable-user-account-smb-sharing-from-terminal-on-mac-os-x
pwpolicy -u "${username}" sethashtypes SMB-NT on
# note that you must use "sudo" to read the exisiting hashtypes for a user using gethashtypes.
# Otherwise, the command silently returns nothing
#
}
# Check if username already exists
if dscl . -read /Users/"${username}" &>/dev/null; then
echo "Error: User \"${username}\" already exists"
exit 1
fi
# Find the next available UID after the current highest UID
# Uses dscacheutil to find highest UID, then verifies with dscl
# Get the highest UID currently in use
highest_uid=$(dscl . -list /Users UniqueID | awk '{print $2}' | sort -n | tail -1)
# Check if we got a valid result
if [ -z "${highest_uid}" ]; then
echo "Error: Could not determine highest UID"
exit 1
fi
echo "Highest UID found: $highest_uid"
# Start checking from the next UID
next_uid=$((highest_uid + 1))
# Keep checking until we find an available UID
while true; do
# Check if this UID exists using dscl
result=$(dscl . -search /Users UniqueID "${next_uid}" 2>/dev/null)
if [ -z "${result}" ]; then
# UID is available
echo -e "Next available UID: ${next_uid}\n"
echo -e "Checking if UID is already in use...\n"
if [ "${next_uid}" -lt 500 ]; then
echo "Error: Refusing to create user with UID < 500"
exit 1
fi
echo -e "Creating sharing-only user \"${username}\" with UID ${next_uid}…\n"
createUser
enableSMB
echo -e "Done."
exit 0
else
# UID exists, try the next one
echo "UID ${next_uid} is already in use, checking next..."
next_uid=$((next_uid + 1))
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment