Skip to main content
RH294

Using Ansible Playbooks

By September 7, 2019September 12th, 2022No Comments

Deploying Ansible Playbooks

Although the use of ad-hoc commands is feasible, being able to store the desired state of our managed nodes is preferable. This gives us both documentation of the configuration and the ability to configure new nodes in the same way with the same commands in the same order. Ansible Playbooks are at the heart of configuration management with Ansible and take the form of YAML files. But before we move to far with Ansible Playbooks, we  need to create a new remote user on the remote managed nodes. We are currently connecting as the root user via SSH to the managed nodes. Ideally, we would want to disable root access via SSH. So it is a must that we create a new user with password-less sudo escalation rights as well as key-based authentication via SSH.


Ansible Playbooks to Deploy Users

Beginning with the very basics we will create an Playbook to deploy the new user account. We will also have a configuration file, an ansible.cfg and inventory file. As is usual, we create a directory for the Playbook and associated resources.

Initial Setup
-----
$ mkdir $HOME/deploy_devops
$ cd $HOME/deploy_devops
$ vim inventory
[redhat]
192.168.122.[4:5]
[ubuntu]
192.168.122.6
$ vim ansible.cfg
[defaults]
inventory = ./inventory
remote_user = root
[privilege_escalation]
become = false
-----

We of course, still have to connect as root until we have deployed the devops user account. This is represented by the ansible.cfg file we use. The inventory file represents the hosts I am using as this module is developed. Make sure you use names or IP Addresses that represent your systems

The YAML file that we create will need to:

  • Create the devops user
  • Deploy the sudoers configuration
  • Deploy the SSH public key of our user account to devops

Using Inventory Variables For Groups Membership with Ansible Playbooks

Using a mix of distributions as a level of complexity to the Playbook that would not be required if we used solely RHEL 8. The exam is most likely to be based n RHEL 8 only, however, reality states that you will come across a mix in the real world. In Debian based systems it is the *sudo* group that is used for administration, in RHEL it is the *wheel* group. Although we will be setting independent access to sudo for the devops users it is pertinent to ensure that they belong to the correct group. Rather than have one play in the Playbook for Ubuntu and one for RHEL systems, we can make use of a single task to create the user and add it to the correct group by using variables. The variable can be created and associated with an Ansible Inventory group directly within the Inventory file. This is what we will use for simplicity. Another option which we show in later modules is by creating a *group_vars* directory within the working directory. The groups_vars directory would contain files based on Inventory group names, these files would host the variables. We will use the former method and will edit the Inventory file directly.

Define Inventory Variable
-----
$ cd $HOME/deploy_devops
$ vim inventory
[redhat]
192.168.122.[4:5]
[redhat:vars]
admin_group=wheel
[ubuntu]
192.168.122.6
[ubuntu:vars]
admin_group=sudo
-----

We have defined the variable admin_group within the inventory file. [redhat:vars] is used to set a list of variable for the group redhat. Likewise with the the ubuntu group we use [ubuntu:vars]. In each we set just the one variable but we could set more. When setting the group membership for the devops user that we will create we will use the variable name.

Playbook to Create User

If we only want to create the user we have seen this using the user module with the ad-hoc commands. We will now expand upon this creating a user in the Playbook. The PLaybook will grow throughout this module as we add more required elements.

Define Basic Playbook
-----
$ cd $HOME/deploy_devops
$ vim deploy_devops.yml
---
- name: Deploy Ansible User
hosts: all
tasks:

- name: Create Devops User
user:
name: devops
groups: "{{ admin_group }}"
append: true
create_home: true
comment: "Ansible Management Account"
expires: -1
...
-----

The Playbook contains a single play, Deploy Ansible User that runs on the built-in group all. The play has a single task currently that creates the user. Using the user module, the keys are explained below, but don’t forget to use : ansible-doc user for more information:

name::
The value of this key is used to name of the user to create, remove or modify. It has an alias of *user* and accepts a string value. We set the login name for the user to be devops int this instance.

groups::
A list of groups that the user will be added to. When set to an empty string, ”, null the user is removed from all groups other than their primary group. The list of group names should be comma separated. Here we add the user to the correct administration group, based on the variable.

append::
This is used with the groups key and ensures that the group list is appended to. This is a good option to use by default unless you specifically want to replace the group list, if it exists.

create_home::
This ensures that the user home directory is created if it does not exist. It also defaults to true. There is an alias of *createhome* as this represents a pre-Ansible 2.5 key name.

comment::
This sets the gecos field for the user and is useful to help document the purpose of the account.

expires::
This should be an expiry epoch value as a float value. We can specify the account not to expire by using -1 as in this case. This has been supported since Ansible 2.6

Once the YAML file is created and take care of the indent levels, all keys under the user module must be indented to the same level, we can run a syntax check, making sure the option for syntax checking at the end of the line makes for easy editing to run the Playbook later:

Syntax Checking the Playbook
-----
$ cd $HOME/deploy_devops
$ ansible-playbook deploy_devops.yml --syntax-check

playbook: deploy_devops.yml
-----

The output shows that there are no syntax errors which is a great start. We can now deploy the Playbook

Executing the Playbook
-----
$ cd $HOME/deploy_devops
$ ansible-playbook deploy_devops.yml
... output omitted
-----

The user should now be created on all three systems with the user being in the correct group. We have not set the user password but we can leave this the way that it is as we will use SSH authentication. For completeness we will add the password for completeness but in later modules we will see how this can be adjusted by using ansible-vault. First though, we will looking at creating a Playbook to reverse any changes.

Reverse Playbook
-----
$ cd $HOME/deploy_devops
$ vim remove_devops.yml
---
- name: Reverse Ansible User
hosts: all
tasks:

- name: Remove Devops User
user:
name: devops
state: absent
remove: true
...

$ ansible-playbook remove_devops.yml
... output omitted
-----

Setting the User Password in Ansible Playbooks

The password key of the user module in Linux accepts only a hashed password, not a clear text value. We can use an ad-hoc command to generate the password hash using the generated value as the user password. This is OK and secure, but we are not documenting what the password is. We can set the password to the result of the hash which then will document the password value but is insecure as anyone who can read the Playbook can read the password. Later will will see how we can be secure and keep the documented password by using ansible-vault. For this demonstration we store the password in the Playbook passing the value through to the hashing function.

Adding a Password
-----
$ cd $HOME/deploy_devops
$ vim deploy_devops.yml
---
- name: Deploy Ansible User
hosts: all
tasks:

- name: Create Devops User
user:
name: devops
groups: "{{ admin_group }}"
append: true
create_home: true
comment: "Ansible Management Account"
expires: -1
password: "{{ 'Password1' | password_hash('sha512','A512') }}"
...

$ ansible-playbook deploy_devops.yml
... output omitted
-----

Technically, there is no need to delete the user each time, if the user is present and in the desired state then ansible does not need to run the task. Deleting the user account will also delete the user password so there is no need to modify the reverse Playbook.

Deploy SSH Public Key in Ansible Playbooks

The user account that you are using, ideally, will be able to connect as the devops user without using a password with SSH. Just as we have already setup for root access, we need to copy the SSH public key of our user account on the controller node to the devops user. We use the authorized_key module to manage this:

Deploy SSH Public Key
-----
$ cd $HOME/deploy_devops
$ vim deploy_devops.yml
---
- name: Deploy Ansible User
hosts: all
tasks:

- name: Create Devops User
user:
name: devops
groups: "{{ admin_group }}"
append: true
create_home: true
comment: "Ansible Management Account"
expires: -1
password: "{{ 'Password1' | password_hash('sha512','A512') }}"

- name: Deploy Local User SSH Key
authorized_key:
user: devops
state: present
manage_dir: true
key: "{{ lookup('file', '/home/tux/.ssh/id_rsa.pub') }}"
...

$ ansible-playbook deploy_devops.yml
... output omitted
-----

As the user account is present on all systems there is no need to modify the devops user but the key will not be present in /home/devops/.ssh/authorized_keys file. The local user’s public key, in my case the user tux, will be copied to the managed nodes.

user::
The user account on the remote system to check for the key. For use it is the devops user on the remote systems.

state::
We are requesting the the desired state is that the key is present in the file

manage_dir::
This allows the module to manage the parent directory is required, .ssh.

key::
The key to copy to the remote system. Check the EXAMPLES section of ansible-doc authorized_key if you have a brain freeze in the practical exam.

There is no need to alter the reverse Playbook as we are deleting the user’s home directory which will delete the authorized_keys file.

Password-less Sudo Access

As well as having no password prompts for SSH we would like the devops user to be able to run sudo without being prompted for their password. It is usual that the default access with the group wheel or sudo would want a password to be entered. To cover this we will add an entry for devops in the /etc/sudoers.d/ directory. The Ansible copy module will work for this:

Configure Password-less Sudo
-----
$ cd $HOME/deploy_devops
$ vim deploy_devops.yml
---
- name: Deploy Ansible User
hosts: all
tasks:

- name: Create Devops User
user:
name: devops
groups: "{{ admin_group }}"
append: true
create_home: true
comment: "Ansible Management Account"
expires: -1
password: "{{ 'Password1' | password_hash('sha512','A512') }}"

- name: Deploy Local User SSH Key
authorized_key:
user: devops
state: present
manage_dir: true
key: "{{ lookup('file', '/home/tux/.ssh/id_rsa.pub') }}"

- name: Setup Sudo Access for Devops User
copy:
dest: /etc/sudoers.d/devops
content: 'devops ALL=(ALL) NOPASSWD: ALL' 
validate: /usr/sbin/visudo -cf %s
...

$ ansible-playbook deploy_devops.yml
... output omitted
-----

Only the third task of the play should change the managed nodes. It is this task that deploys the new sudoers file to the nodes.

dest::
This is the target file that we want to copy the source file to. In this instance we do not specify the *src*, an actual local file. As we only need a single line of content we specify the content directly in the task.

content::
This is a single line, in this case, that we use to populate the destination file. This allows the devops user to run any command without the need of entering their password.

validate::
We can use visudo to validate the file before the copy is complete. This is the safest behavior even if we are certain the content is correct. An incorrect entry into the sudoers environment can prevent sudo from working for any account.

Final Reverse Playbook

Especially in development environments it is very useful to be able to reverse all Ansible Playbooks we create. We can easily put them back into pristine condition for another project or test.

NOTE: At this stage, don't run the reverse playbook as want the devops account to be present with the configuration we have made.
Final Reverse Playbook
-----
$ cd $HOME/deploy_devops
$ vim remove_devops.yml
---
- name: Reverse Ansible User
hosts: all
tasks:

- name: Remove Devops User
user:
name: devops
state: absent
remove: true

- name: Remove Sudo Entry
file:
path: /etc/sudoers.d/devops
sate: absent
...
-----

As mentioned, we do not want to run the reverse playbook at this stage as the devops user is in the state that we want. Feel free to run the reverse so you can check that it is working but don’t forget to deploy the user again.

Summary

We wanted to deploy a new user account to each system that we can use to ensure that we do not use the root account for Ansible operations. We create the devops user that can be access using the SSH keys of out current user account. The devops user has password-less access to sudo on the managed nodes. We usually will want to create two Playbooks for each deployment, one to carry out the required work and another that we can use to reverse the changes. The video follows