Use the lookup and query functions to template data from external sources into playbooks and deployed template files.
Outcomes
Identify different sources of information in Ansible Playbooks.
Use existing lookup plug-ins to retrieve information.
As the student user on the workstation machine, use the lab command to prepare your system for this exercise.
This command creates the Git repository needed for the exercise.
[student@workstation ~]$ lab start data-lookups
Procedure 7.2. Instructions
Clone the https://git.lab.example.com/student/data-lookups.git Git repository into the /home/student/git-repos directory and then create a branch for this exercise.
From a terminal, create the /home/student/git-repos directory if it does not already exist, and then change into it.
[student@workstation ~]$mkdir -p ~/git-repos/[student@workstation ~]$cd ~/git-repos/
Clone the https://git.lab.example.com/student/data-lookups.git repository and then change directory to the cloned repository:
[student@workstation git-repos]$git clone \>https://git.lab.example.com/student/data-lookups.gitCloning into 'data-lookups'... ...output omitted... [student@workstation git-repos]$cd data-lookups
Create the exercise branch.
[student@workstation data-lookups]$ git checkout -b exercise
Switched to a new branch 'exercise'Populate site.yml with tasks to create groups using YAML-formatted data from the groups.yml file.
Review the groups.yml file.
---
- name: devs
members:
- user1
- user2
- name: ops
members:
- user3You can read the contents of a YAML-formatted file in several ways.
In the "Load group information" task from the site.yml playbook, you use the ansible.builtin.file lookup plug-in to read the file, and the from_yaml filter to load its data into the user_groups fact as structured YAML so that Ansible can parse it later:
"{{ lookup('ansible.builtin.file', 'groups.yml') | from_yaml }}"The completed task should consist of the following content:
- name: Load group information
ansible.builtin.set_fact:
user_groups: "{{ lookup('ansible.builtin.file', 'groups.yml') | from_yaml }}"Complete the "Create groups" task to create each group specified by the YAML data in the user_groups fact.
The task must use the ansible.builtin.group module, looping on the user_groups fact.
Use item.name to specify the group name of the group to create for each iteration of the loop.
The completed task should consist of the following content:
- name: Create groups
ansible.builtin.group:
name: "{{ item['name'] }}"
state: present
loop: "{{ user_groups }}"Complete the "Create users" task.
Use the lines lookup plug-in to read user information from the users.txt file.
"{{ query('ansible.builtin.lines', 'cat users.txt') }}"The lines plug-in reads the output of cat users.txt as individual lines.
The query function ensures that the data is a list.
Configure the task to loop through the users in this list.
Use the loop keyword to configure the task to loop through the users in this list:
- name: Create users
ansible.builtin.debug:
msg: "To be done"
loop: "{{ query('ansible.builtin.lines', 'cat users.txt') }}"When you create the users, create a random password as well.
The password lookup plug-in generates random passwords and optionally stores the passwords in a local file.
The previous step added a loop on the list of usernames from users.txt; you can use the same item variable to generate the associated file for each user.
"{{ lookup('ansible.builtin.password', 'credentials/' + item + ' length=9') }}"Store the password into a task variable to make it easier to use.
Ensure that you include the space after the first single quote in the ' length=9' argument.
The task should now consist of the following content:
- name: Create users
vars:
password_plain: "{{ lookup('ansible.builtin.password', 'credentials/' + item + ' length=9') }}"
ansible.builtin.debug:
msg: "To be done"
loop: "{{ query('ansible.builtin.lines', 'cat users.txt') }}"Replace the ansible.builtin.debug module with the ansible.builtin.user module.
You need to hash the new password before you can use it.
You also need to set update_password: on_create for the module.
The update_password: on_create option only sets the password if the task creates a new user on the managed host.
If the user already exists but the password specified by the playbook is different from the one already set for the user on the system, then Ansible does not change the user's password.
This helps ensure that Ansible does not change an existing user's password if that user has already changed it but you run the playbook a second time.
- name: Create users
vars:
password_plain: "{{ lookup('ansible.builtin.password', 'credentials/' + item + ' length=9') }}"
ansible.builtin.user:
name: "{{ item }}"
password: "{{ password_plain | password_hash('sha512') }}"
update_password: on_create
state: present
loop: "{{ query('ansible.builtin.lines', 'cat users.txt') }}"The last task uses the subelements filter to populate the members of each group.
However, no lookup plug-ins are used.
Confirm that the completed play consists of the following content:
- name: Populate users and groups
hosts: all
gather_facts: false
tasks:
- name: Load group information
ansible.builtin.set_fact:
user_groups: "{{ lookup('ansible.builtin.file', 'groups.yml') | from_yaml }}"
- name: Create groups
ansible.builtin.group:
name: "{{ item['name'] }}"
state: present
loop: "{{ user_groups }}"
- name: Create users
vars:
password_plain: "{{ lookup('ansible.builtin.password', 'credentials/' + item + ' length=9') }}"
ansible.builtin.user:
name: "{{ item }}"
password: "{{ password_plain | password_hash('sha512') }}"
update_password: on_create
state: present
loop: "{{ query('ansible.builtin.lines', 'cat users.txt') }}"
- name: Add users to groups
ansible.builtin.user:
name: '{{ item[1] }}'
groups: "{{ item[0]['name'] }}"
append: true
loop: "{{ user_groups | subelements('members', 'skip_missing=true') }}"Run the playbook and verify that the users, groups, and group members are configured correctly.
Use the ansible-navigator run command to run the playbook.
[student@workstation data-lookups]$ansible-navigator run \>-m stdout site.ymlPLAY [Populate users and groups] *********************************************** TASK [Load group information] ************************************************** ok: [serverf.lab.example.com] TASK [Create groups] *********************************************************** changed: [serverf.lab.example.com] => (item={'name': 'devs', 'members': ['user1', 'user2']}) changed: [serverf.lab.example.com] => (item={'name': 'ops', 'members': ['user3']}) TASK [Create users] ************************************************************ changed: [serverf.lab.example.com] => (item=user1) changed: [serverf.lab.example.com] => (item=user2) changed: [serverf.lab.example.com] => (item=user3) TASK [Add users to groups] ***************************************************** changed: [serverf.lab.example.com] => (item=[{'name': 'devs', 'members': ['user1', 'user2']}, 'user1']) changed: [serverf.lab.example.com] => (item=[{'name': 'devs', 'members': ['user1', 'user2']}, 'user2']) changed: [serverf.lab.example.com] => (item=[{'name': 'ops', 'members': ['user3']}, 'user3']) PLAY RECAP ********************************************************************* serverf.lab.example.com : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Verify that the users have been created on the serverf server.
[student@workstation data-lookups]$ ssh serverf "tail -n3 /etc/passwd"
user1:x:1002:1004::/home/user1:/bin/bash
user2:x:1003:1005::/home/user2:/bin/bash
user3:x:1004:1006::/home/user3:/bin/bashVerify that the groups exist and have the relevant members.
[student@workstation data-lookups]$ ssh serverf "tail -n5 /etc/group"
devs:x:1002:user1,user2
ops:x:1003:user3
user1:x:1004:
user2:x:1005:
user3:x:1006: