8i | 9i | 10g | 11g | 12c | 13c | 18c | 19c | 21c | Misc | PL/SQL | SQL | RAC | WebLogic | Linux

Home » Articles » Misc » Here

Ansible : Roles - First Steps

Roles allow us to split Ansible playbooks into more manageable chunks, rather than having one monolithic playbook. They also aid in code reusability.

Related articles.

Useful Resources

There is a vagrant build for the virtual machines used in these examples here.

There is a GitHub repository of the scripts used in the examples here.

Assumptions

This article assumes you have a basic understanding of Ansible and Ansible playbooks. You can read introductory articles on these here.

Role Directories

From our working directory, we create a directory called roles with three subdirectories representing three separate roles.

Each role has a "tasks" subdirectory. There is also a "files" directory for the "appservers" role.

mkdir -p roles/base/tasks
mkdir -p roles/appservers/tasks
mkdir -p roles/appservers/files
mkdir -p roles/databases/tasks

The roles will be described below.

Base Role

We create a new file called "main.yml" in the "roles/base/tasks" directory with the tasks we want to perform on all servers. In this example we have three tasks. The first two are distribution dependent updates of all the packages on the server, and the third is a conditional reboot of the server if any changes have happened. Notice we don't reboot the server if it is called "ansible-server.localdomain", as that is our Ansible control host, so rebooting it in the middle of running a playbook would be a bad idea. The contents of this file looks like a playbook, but without the header section that specifies the hosts. It is only the task definitions.

- name: Update all packages (DNF)
  dnf:
    name: "*"
    update_cache: yes
    state: latest
  when: ansible_distribution in ["OracleLinux", "Red Hat Enterprise Linux", "CentOS"]
  register: dnf_update

- name: Update all packages (APT)
  dnf:
    name: "*"
    update_cache: yes
    state: latest
  when: ansible_distribution in ["Ubuntu", "Debian"]
  register: atp_update

- name: Reboot server
  reboot:
  when: (dnf_update.changed or atp_update.changed) and inventory_hostname != "ansible-server.localdomain"

Appservers Role

We create a new file called "main.yml" in the "roles/appservers/tasks" directory with the tasks we want to perform on all application servers. These tasks install NGINX, configure the firewall and copy in a default page into place.

- name: Install NGINX package (DNF)
  dnf:
    name: nginx
    state: present
    update_cache: yes
  when: ansible_distribution in ["OracleLinux", "Red Hat Enterprise Linux", "CentOS"]

- name: Install NGINX package (APT)
  apt:
    name: nginx
    state: present
    update_cache: yes
  when: ansible_distribution in ["Ubuntu", "Debian"]

- name: Enable and start NGINX service
  service:
    name: nginx
    enabled: yes
    state: started

- name: Allow SSH traffic through the firewall
  firewalld:
    service: "{{ item }}"
    permanent: yes
    state: enabled
  with_items:
    - ssh
    - https

- name: Enable the firewall
  service:
    name: firewalld
    enabled: yes
    state: started

- name: Copy default web page
  copy:
    src: default_page.html
    dest: /usr/share/nginx/html/index.html
    owner: root
    group: root
    mode: 0644

We also need to create the "default_page.html" file in the "roles/appservers/files" directory with the following contents.

<html>
  <title>Default Page</title>
  <body>
    <p>This is the default page!</p>
  </body>
</html>

Databases Role

We create a new file called "main.yml" in the "roles/databases/tasks" directory with the tasks we want to perform on all database servers. These tasks create the groups and user for an Oracle installation.

- name: Create database groups
  group:
    gid: "{{ item.group_id}}"
    name: "{{ item.group_name}}"
    state: present
  with_items:
    - { group_name: oinstall , group_id: 54321} 
    - { group_name: dba, group_id: 54322} 
    - { group_name: oper, group_id: 54323 }

- name: Create oracle user
  user:
    uid: 54321
    name: oracle
    password: "{{ 'DummyPassword123' | password_hash('sha512', 'mysecretsalt') }}"
    groups: oinstall,dba,oper
    append: yes
    state: present
    update_password: on_create

Playbook Using Roles

With the roles created we can build a new playbook that uses the roles. In the top-level working directory we create a playbook called "site.yml" with the following contents. We have a separate play for each role, with the play determining which hosts the role is executed against.

---
- name: Base role for all servers
  hosts: all
  become: true
  roles:
    - base

- name: Base role for application servers
  hosts: appservers
  become: true
  roles:
    - appservers

- name: Base role for database servers
  hosts: databases
  become: true
  roles:
    - databases

We run the playbook, which performs all the tasks in the roles for the required servers. These are repeats of task we've performed in the Ansible : Playbooks - First Steps article, so there are no resulting changes.

$ ansible-playbook site.yml

PLAY [Base role for all servers] ***************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [database1.localdomain]
ok: [appserver1.localdomain]
ok: [appserver2.localdomain]
ok: [ansible-server.localdomain]

TASK [base : Update all packages (DNF)] ********************************************************************************************************
ok: [appserver1.localdomain]
ok: [appserver2.localdomain]
ok: [database1.localdomain]
ok: [ansible-server.localdomain]

TASK [base : Update all packages (APT)] ********************************************************************************************************
skipping: [database1.localdomain]
skipping: [appserver1.localdomain]
skipping: [ansible-server.localdomain]
skipping: [appserver2.localdomain]

TASK [base : Reboot server] ********************************************************************************************************************
skipping: [ansible-server.localdomain]
skipping: [appserver1.localdomain]
skipping: [appserver2.localdomain]
skipping: [database1.localdomain]

PLAY [Base role for application servers] *******************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [appserver2.localdomain]
ok: [appserver1.localdomain]

TASK [appservers : Install NGINX package (DNF)] ************************************************************************************************
ok: [appserver1.localdomain]
ok: [appserver2.localdomain]

TASK [appservers : Install NGINX package (APT)] ************************************************************************************************
skipping: [appserver1.localdomain]
skipping: [appserver2.localdomain]

TASK [appservers : Enable and start NGINX service] *********************************************************************************************
ok: [appserver1.localdomain]
ok: [appserver2.localdomain]

TASK [appservers : Allow SSH traffic through the firewall] *************************************************************************************
ok: [appserver2.localdomain] => (item=ssh)
ok: [appserver1.localdomain] => (item=ssh)
ok: [appserver2.localdomain] => (item=https)
ok: [appserver1.localdomain] => (item=https)

TASK [appservers : Enable the firewall] ********************************************************************************************************
ok: [appserver1.localdomain]
ok: [appserver2.localdomain]

PLAY [Base role for database servers] **********************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************
ok: [database1.localdomain]

TASK [databases : Create database groups] ******************************************************************************************************
ok: [database1.localdomain] => (item={'group_name': 'oinstall', 'group_id': 54321})
ok: [database1.localdomain] => (item={'group_name': 'dba', 'group_id': 54322})
ok: [database1.localdomain] => (item={'group_name': 'oper', 'group_id': 54323})

TASK [databases : Create oracle user] **********************************************************************************************************
ok: [database1.localdomain]

PLAY RECAP *************************************************************************************************************************************
ansible-server.localdomain : ok=2    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0
appserver1.localdomain     : ok=7    changed=0    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
appserver2.localdomain     : ok=7    changed=0    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0
database1.localdomain      : ok=5    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

$

We see a separate play for each role, along with all the tasks in the role.

Once the roles are defined, there is nothing to stop us creating multiple playbooks that reference these roles, or a subset of them.

Documenting Roles

It would make sense to create a "README.md" file to document each role. We'll create a simple example for each role below.

cat > roles/base/README.md <<EOF
# Base Role

This role contains tasks we expect to run on all servers.
EOF


cat > roles/appservers/README.md <<EOF
# Appservers Role

This role contains tasks we expect to run on all application servers.
EOF


cat > roles/databases/README.md <<EOF
# Databases Role

This role contains tasks we expect to run on all database servers.
EOF

For more information see:

Hope this helps. Regards Tim...

Back to the Top.