The Scenario: The Help Desk Bottleneck
From 2019 to 2021, while serving as Lead Backend Software Engineer at a fast-growing company, I occasionally support our Linux System Administration tasks. When the DevOps team encountered a critical bottleneck during an initiative to scale dozens of new server deployments, I stepped in to streamline the infrastructure processes.
The DevOps team was being hampered by constant, fragmented requests from the help desk to manually create new Linux accounts for recruits testing the latest application. These interruptions were not only time-consuming but were directly preventing the team from focusing on the high-priority infrastructure deployments that define their core responsibilities.
I realized that we weren't just struggling with a task; we were struggling with a scaling bottleneck. To regain the team's focus and ensure we hit our project deadlines, I decided to automate this workflow.
The First Step: The Interactive Script
My first objective was to develop a robust, automated shell script to efficiently create new Linux user accounts. I started with an interactive Bash script (create-user-interactive.sh) that prompted for input.
This was a good educational exercise for learning the fundamentals of Bash—like useradd, passwd, and shell variables. However, I quickly learned that while interactive scripts are great for learning, they are rarely used in professional DevOps environments.
Why Manual Scripts Don’t Scale
As I transitioned into a more infrastructure-focused role, I realized that manual scripts fail for three key reasons:
Lack of Automation: DevOps is about "Infrastructure as Code" (IaC). Asking an engineer to sit at a terminal and type prompts is slow, error-prone, and destroys the ability to automate.
Lack of Centralization: In a real team, we aren't creating users on individual local machines. We manage identity across hundreds of servers.
Security Risks: Hardcoding passwords or piping them through echo is a major red flag.
The Industry Standard: How We Actually Do It
If you are working in a industry-standard DevOps team, you don't use manual scripts for user management. You use one of the following methods:
Configuration Management (Ansible, Puppet): We define the state of the user in a configuration file. Ansible is my favorite here because it is idempotent—if the user already exists, it does nothing; if they are missing, it creates them.
Centralized Identity (LDAP, Active Directory): In an enterprise, we connect servers to a central directory. When an employee leaves, we disable their account in one place, and they lose access everywhere instantly.
Cloud-Native IAM (AWS/GCP IAM): For cloud infrastructure, we often skip OS-level accounts entirely, using services like AWS SSM Session Manager to connect to instances without managing local users or SSH keys.
The Pragmatic Solution: create-user-automated.sh
Sometimes, you still need a shell script. Perhaps you are working on a small project or need a bootstrap script for a server's first boot. If you must use a script, make it non-interactive so it can be automated by a CI/CD pipeline.
Here is the "DevOps" way to write that script (create-user-automated.sh):
#!/bin/bash
#
# Name: create-user-automated.sh
# Description: Creates a new user on the local system non-interactively with a generated password.
# Usage: ./create-user-automated.sh <username> <full_name>
# Example call: ./create-user-automated.sh dak "Dakota"
# Ensure the script is run as root
if [[ "${UID}" -ne 0 ]]; then
echo 'Error: Please run with sudo or as root.' >&2
exit 1
fi
# Check for correct number of arguments
if [[ "${#}" -ne 2 ]]; then
echo "Usage: ${0} <username> <full_name>" >&2
exit 1
fi
USER_NAME="${1}"
COMMENT="${2}"
# Check if user already exists to maintain idempotency
if id "${USER_NAME}" &>/dev/null; then
echo "User ${USER_NAME} already exists. Skipping creation."
exit 0
fi
# Generate a random password (12 characters, alphanumeric + special characters)
PASSWORD=$(date +%s%N${RANDOM}${RANDOM} | sha256sum | head -c 12)
# Create the account
useradd -c "${COMMENT}" -m "${USER_NAME}"
if [[ "${?}" -ne 0 ]]; then
echo "Error: Could not create account for ${USER_NAME}." >&2
exit 1
fi
# Set the generated password
echo "${PASSWORD}" | passwd --stdin "${USER_NAME}" &>/dev/null
if [[ "${?}" -ne 0 ]]; then
echo "Error: Could not set password for ${USER_NAME}." >&2
exit 1
fi
# Force password change on first login
passwd -e "${USER_NAME}" &>/dev/null
# Display output block for the Help Desk
cat <<EOF
------------------------------------------------------------
ACCOUNT PROVISIONING SUCCESSFUL
------------------------------------------------------------
System: ${HOSTNAME}
User: ${USER_NAME}
Temp Password: ${PASSWORD}
IMPORTANT:
The user will be required to change this password upon
their first successful login. Please forward these
credentials to the user securely.
------------------------------------------------------------
EOF
exit 0
The Engineer’s Mindset
Moving from a manual, interactive script to an automated, idempotent one isn't just about writing cleaner code—it’s about a change in mindset. It’s about building systems that don't require our constant presence to function.
By building this tool, I empowered the help desk to handle requests independently, ensured our provisioning was consistent and error-free, and most importantly, I regained the time I needed to focus on our high-priority server deployments.
Top comments (0)