Adding Users

(Note: the following instructions apply mostly to Mac OS X operating systems. If you want to alter it for Windows or Linux, check out this link or contact me.)

When managing remote systems, you’ll probably want to create a custom user to avoid using “root” for security purposes. Or, maybe you just want multiple people to access the system. Either way, let’s automate your user-creation need with Ansible.

Generating a Password

Passwords are supposed to be kept secret, so we have to do some legwork to keep passwords secure. There are features of Ansible that encrypt sensitive data on our local machine, but we’ll cover that in a later lesson. For now, we need to focus our attention on the password that we will be sending to the remote systems.

For that, we need to hash the password through a Python script. Unfortunate, I know. Hopefully Ansible will come up with a better solution for hashing the password without this script since it’s also built in Python. If you know an easier way, please let me know.

Start by installing these Python libraries via the command line:

$ easy_install pip;
$ pip install passlib;

Now, for the Python script. Save this in the local directory as password_hasher.py

#!/usr/bin/env python

# Import the SHA256 Hash Algorithm
import sys
from passlib.hash import sha256_crypt

# Want a prompt instead of CLI?
# Comment out the "Grab from CLI" block.
# Then use this code instead to get a prompt:
#
# from getpass import getpass
# print "Type your desired password"
# password = getpass()

# Grab from CLI
if len(sys.argv) != 2:
  print "Please provide one argument: the plain-text password"
  exit()
password = sys.argv[1]

# Generate a new salt and hash the provided password
hash = sha256_crypt.encrypt(password);

# Output
print hash

This script hashes a provided plain-text password into a format Linux can understand. One important thing to know is, even if the plain text password is the same, you might end up with different hashes due to a random number of “passes” done to further secure the password contents. Now, onto the Ansible playbook.

The Playbook

Open up vagrant.yml again, (clear out the other tasks if you like) and add the following two sections. Your playbook should have a new section in it called “vars” that looks similar to the below playbook where we specify our user and password that we want created.

---

- name: Create a User
  hosts: all
  vars:
    user_username: ansible
    user_password: mysecretword
    user_passfile: ./password.hash
  tasks:

    [... CLIPPED ...]

Now, to encrypt the password with the following task…

- name: Generate User Password file
  shell: python ./password_hasher.py {{ user_password }} > {{ user_passfile }}
  args:
    creates: "{{ user_passfile }}"
  delegate_to: localhost

Here’s where we invoke the Python script with a shell. This is exactly like your local command prompt (Bash probably), and you can run any command here. We’re making use of the variables from the “vars” section higher up in the playbook to substitute in values.

We use “creates” under args to show that this script creates the password hash file, and does not need to be run again if that file already exists. Again, focus on the state you want the machine to be in, not the commands that need to be run. We also use a new parameter called “delegate” to say that this command should run on our local machine where our password file should be stored, as opposed to the remote machine

- name: Ensure User is Present
  user: name={{ user_username }} password={{ lookup('file', user_passfile) }} group=sudo generate_ssh_key=yes shell="/bin/bash" state=present
  sudo: yes

After we have that file, we just supply the “user” module with expected parameters, notably group=”sudo” to ensure the new user has the correct permissions to perform administrative tasks. We look up the password file contents and substitute that in when creating the new user.

Final Playbook

---

- name: Create a User
  hosts: all
  vars:
    user_username: ansible
    user_password: mysecretword
    user_passfile: ./password.hash
  tasks:

    - name: Generate User Password file
      shell: python ./password_hasher.py {{ user_password }} > {{ user_passfile }}
      args:
        creates: "{{ user_passfile }}"
      delegate_to: localhost

    - name: Ensure User is Present
      user: name={{ user_username }} password={{ lookup('file', user_passfile) }} group=sudo generate_ssh_key=yes shell="/bin/bash" state=present
      sudo: yes

That’s it! Run with “vagrant provision” and you can then connect as your new user with this command since Vagrant runs SSH on port 2200 instead of the default 22:

$ ssh -p 2200 [user]@localhost