Simple Per-User Bash Profile Configuration
While creating Ansible scripts to automate the configuration of servers, I frequently stumble across an issue where I need to setup a user's profile on the server that has a custom path.
The issue that arises is that the .bash_profile
file is a single file where any number of PATH
exports can be provided. Any step in a provisioning tool like Ansible should be aware that this only adds state. I have seen other DevOps workers use modules like lineinfile
, which I abhor because you can never be certain that the module will work. You will always ask yourself, "did the PATH on line 5 get set correctly, or did someone change one character (or perhaps add a comment) that now breaks the lineinfile module task?"
When I write scripts I adhere to the "tell, don't ask" principle 1. By using lineinfile
, you are asking the system if the line exists and, if it is missing, add the line. I much prefer telling the system, "here is your configuration file, just load it." If you ask a system what state it is in, you cannot be certain that it will be in the state later. If you simply tell it what state it should be in, that state shouldn't change (state changes can be mitigated by isolating it to a very small component).
To remove state we need a way to specify single-purpose .bash_profile
files on a per-user basis. My solution to this is to create a .bash_profile.d
directory that will contain these files, then have the .bash_profile
file only contain the code to load each file found in the configuration subdirectory.
# .bash_profile
BASH_PROFILE_CONF_DIR="$HOME/.bash_profile.d"
if [ ! -d $BASH_PROFILE_CONF_DIR ]; then
# create the configuration directory
mkdir $BASH_PROFILE_CONF_DIR
chmod 700 $BASH_PROFILE_CONF_DIR
# add a dummy file so the `ls` in the loop doesn't err out
touch $BASH_PROFILE_CONF_DIR/default
fi
for profile_file in `ls .bash_profile.d/*`; do
source $profile_file
done
Add this script into the user's .bash_profile
2 and then re-login (or simply source ~/.bash_profile
) to execute the script. It will first check for the existence of the configuration directory and create it if it is missing. Then it loops through any files in the subdirectory and loads them into the environment.
Note that a dummy configuration file default
is stored in the configuration directory. This is to prevent any errors from occuring when the script runs the ls
command.
Now when writing Ansible scripts, all you need to do is add a file into the .bash_profile.d
directory and you are done. Once Ansible moves to the next task, a new SSH connection will be spawned and the profile will be updated with the new configuration.
Footnotes
-
"Tell, Don't Ask" is a programming concept, but it works equally well when building systems. ↩
-
This is only on the
.bash_profile
so if you want it on the.bashrc
you will need to add it there as well. ↩