8i | 9i | 10g | 11g | 12c | 13c | 18c | 19c | 21c | 23ai | Misc | PL/SQL | SQL | RAC | WebLogic | Linux
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.
- Useful Resources
- Assumptions
- Role Directories
- Base Role
- Appservers Role
- Databases Role
- Playbook Using Roles
- Documenting Roles
Related articles.
- Ansible Playbooks : Roles
- Ansible : First Steps
- Ansible : Playbooks - First Steps
- Ansible : All 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.
- base : A role for tasks performed on every server.
- appservers : A role for tasks performed only on application servers.
- databases : A role for tasks performed only on database servers.
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:
- Roles
- Ansible Playbooks : Roles
- Ansible : First Steps
- Ansible : Playbooks - First Steps
- Ansible : All Articles
Hope this helps. Regards Tim...