diff --git a/exercises/ansible_rhel/1.3-playbook/README.md b/exercises/ansible_rhel/1.3-playbook/README.md index add63ded4..cf3479a50 100644 --- a/exercises/ansible_rhel/1.3-playbook/README.md +++ b/exercises/ansible_rhel/1.3-playbook/README.md @@ -6,112 +6,488 @@ ## Table of Contents - [Workshop Exercise - Writing Your First Playbook](#workshop-exercise---writing-your-first-playbook) + - [Table of Contents](#table-of-contents) - [Objective](#objective) - [Guide](#guide) - [Step 1 - Playbook Basics](#step-1---playbook-basics) - - [Step 2 - Creating Your Playbook](#step-2---creating-your-playbook) + - [Step 2 - Creating a Directory Structure and File for your Playbook](#step-2---creating-a-directory-structure-and-file-for-your-playbook) - [Step 3 - Running the Playbook](#step-3---running-the-playbook) - - [Step 4 - Checking the Playbook](#step-4---checking-the-playbook) - + - [Step 4 - Extend your Playbook: Start \& Enable Apache](#step-4---extend-your-playbook-start--enable-apache) + - [Step 5 - Extend your Playbook: Create an web.html](#step-5---extend-your-playbook-create-an-webhtml) + - [Step 6 - Practice: Apply to Multiple Host](#step-6---practice-apply-to-multiple-host) ## Objective -In this exercise, you'll use Ansible to conduct basic system setup tasks on a -Red Hat Enterprise Linux server. You will become familiar with fundamental -Ansible modules like `dnf` and `user`, and learn how to create and run -playbooks. +This exercise covers using Ansible to build two Apache web servers on Red Hat Enterprise Linux. This exercise covers the following Ansible fundamentals: + +* Understanding Ansible module parameters +* Understanding and using the following modules + * [dnf module](https://docs.ansible.com/ansible/latest/modules/dnf_module.html) + * [service module](https://docs.ansible.com/ansible/latest/modules/service_module.html) + * [copy module](https://docs.ansible.com/ansible/latest/modules/copy_module.html) +* Understanding [Idempotence](https://en.wikipedia.org/wiki/Idempotence) and how Ansible modules can be idempotent ## Guide -Playbooks in Ansible are essentially scripts written in YAML format. They are -used to define the tasks and configurations that Ansible will apply to your -servers. +Playbooks are files which describe the desired configurations or steps to implement on managed hosts. Playbooks can change lengthy, complex administrative tasks into easily repeatable routines with predictable and successful outcomes. + +A playbook can have multiple plays and a play can have one or multiple tasks. In a task a *module* is called, like the modules in the previous chapter. The goal of a *play* is to map a group of hosts. The goal of a *task* is to implement modules against those hosts. + +> **Tip** +> +> Here is a nice analogy: When Ansible modules are the tools in your workshop, the inventory is the materials and the Playbooks are the instructions. ### Step 1 - Playbook Basics -First, create a text file in YAML format for your playbook. Remember: -- Start with three dashes (`---`). -- Use spaces, not tabs, for indentation. -Key Concepts: -- `hosts`: Specifies the targets for your playbook. -- `tasks`: The actions Ansible will perform. -- `become`: Allows privilege escalation (running tasks with elevated privileges). +Playbooks are text files written in YAML format and therefore need: + +* to start with three dashes (`---`) + +* proper indentation using spaces and **not** tabs\! + +There are some important concepts: + +* **hosts**: the managed hosts to perform the tasks on + +* **tasks**: the operations to be performed by invoking Ansible modules and passing them the necessary options + +* **become**: privilege escalation in playbooks + +> **Warning** +> +> The ordering of the contents within a Playbook is important, because Ansible executes plays and tasks in the order they are presented. + +A Playbook should be **idempotent**, so if a Playbook is run once to put the hosts in the correct state, it should be safe to run it a second time and it should make no further changes to the hosts. -> NOTE: An Ansible playbook is designed to be idempotent, meaning if you run it multiple times on the same hosts, it ensures the desired state without making redundant changes. +> **Tip** +> +> Most Ansible modules are idempotent, so it is relatively easy to ensure this is true. -### Step 2 - Creating Your Playbook -Before creating your first playbook, ensure you are in the correct directory by changing to `~/lab_inventory`: +### Step 2 - Creating a Directory Structure and File for your Playbook + +Enough theory, it’s time to create your first Ansible playbook. In this lab you create a playbook to set up an Apache web server in three steps: + +1. Install httpd package +2. Enable/start httpd service +3. Copy over an web.html file to each web host + +This Playbook makes sure the package containing the Apache web server is installed on `node1`. + +There is a [best practice](https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html) on the preferred directory structures for playbooks. We strongly encourage you to read and understand these practices as you develop your Ansible ninja skills. That said, our playbook today is very basic and creating a complex structure will just confuse things. + +Instead, we are going to create a very simple directory structure for our playbook, and add just a couple of files to it. + +On your control host **ansible**, create a directory called `ansible-files` in your home directory and change directories into it: ```bash -cd ~/lab_inventory +[student@ansible-1 ~]$ mkdir ansible-files +[student@ansible-1 ~]$ cd ansible-files/ ``` -Now create a playbook named `system_setup.yml` to perform basic system setup: -- Update all security related packages. -- Create a new user named ‘myuser’. +Add a file called `apache.yml` with the following content. As discussed in the previous exercises, use `vi`/`vim` or, if you are new to editors on the command line, check out the [editor intro](../0.0-support-docs/editor_intro.md) again. + +```yaml +--- +- name: Apache server installed + hosts: node1 + become: yes +``` -The basic structure looks as follows: +This shows one of Ansible’s strengths: The Playbook syntax is easy to read and understand. In this Playbook: + +* A name is given for the play via `name:`. +* The host to run the playbook against is defined via `hosts:`. +* We enable user privilege escalation with `become:`. + +> **Tip** +> +> You obviously need to use privilege escalation to install a package or run any other task that requires root permissions. This is done in the Playbook by `become: yes`. + +Now that we've defined the play, let's add a task to get something done. We will add a task in which dnf will ensure that the Apache package is installed in the latest version. Modify the file so that it looks like the following listing: ```yaml --- -- name: Basic System Setup +- name: Apache server installed hosts: node1 - become: true + become: yes tasks: - - name: Update all security-related packages + + - name: Install Apache ansible.builtin.dnf: - name: '*' - state: latest - security: true - - - name: Create a new user - ansible.builtin.user: - name: myuser - state: present - create_home: true + name: httpd ``` -> NOTE: Updating the packages may take a few minutes prior to the Ansible playbook completing. +> **Tip** +> +> Since playbooks are written in YAML, alignment of the lines and keywords is crucial. Make sure to vertically align the *t* in `task` with the *b* in `become`. Once you are more familiar with Ansible, make sure to take some time and study a bit the [YAML Syntax](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html). + +In the added lines: -* About the `dnf` module: This module is used for package management with DNF (Dandified YUM) on RHEL and other Fedora-based systems. +* We started the tasks part with the keyword `tasks:`. +* A task is named and the module for the task is referenced. Here it uses the `dnf` module. +* Parameters for the module are added: + * `name:` to identify the package name + * `state:` to define the wanted state of the package -* About the `user` module: This module is used to manage user accounts. +> **Tip** +> +> The module parameters are individual to each module. If in doubt, look them up again with `ansible-doc`. + +Save your playbook and exit your editor. ### Step 3 - Running the Playbook -Execute your playbook using the `ansible-navigator` command: +With the introduction of Ansible Automation Platform 2, several new key components are being introduced as a part of the overall developer experience. Execution environments have been introduced to provide predictable environments to be used during automation runtime. All collection dependencies are contained within the execution environment to ensure that automation created in development environments runs the same as in production environments. + +What do you find within an execution environment? + +* RHEL UBI 8 +* Ansible 2.9 or Ansible Core 2.11 +* Python 3.8 +* Any content Collections +* Collection python or binary dependencies. + +Why use execution environments? + +They provide a standardized way to define, build and distribute the environments that the automation runs in. In a nutshell, Automation execution environments are container images that allow for easier administration of Ansible by the platform administrator. + +Considering the shift towards containerized execution of automation, automation development workflow and tooling that existed before Ansible Automation Platform 2 have had to be reimagined. In short, `ansible-navigator` replaces `ansible-playbook` and other `ansible-*` command line utilities. + +With this change, Ansible playbooks are executed using the `ansible-navigator` command on the control node. + +The prerequisites and best practices for using `ansible-navigator` have been done for you within this lab. + +These include: +* Installing the `ansible-navigator` package +* Creating a default settings `/home/student/.ansible-navigator.yml` for all your projects (optional) +* All execution environment (EE) logs are stored within `/home/student/.ansible-navigator/logs/ansible-navigator.log` +* Playbook artifacts are saved under `/tmp/artifact.json` + +For more information on the [Ansible navigator settings](https://github.com/ansible/ansible-navigator/blob/main/docs/settings.rst) + +> **Tip** +> +> The parameters for ansible-navigator maybe modified for your specific environment. The current settings use a default `ansible-navigator.yml` for all projects, but a specific `ansible-navigator.yml` can be created for each project and is the recommended practice. + +To run your playbook, use the `ansible-navigator run ` command as follows: ```bash -[student@ansible-1 lab_inventory]$ ansible-navigator run system_setup.yml -m stdout +[student@ansible-1 ansible-files]$ ansible-navigator run apache.yml ``` -Review the output to ensure each task is completed successfully. +> **Tip** +> +> The existing `ansible-navigator.yml` file provides the location of your inventory file. If this was not set within your `ansible-navigator.yml` file, the command to run the playbook would be: `ansible-navigator run apache.yml -i /home/student/lab_inventory/hosts` + +When running the playbook, you'll be displayed a text user interface (TUI) that displays the play name among other information about the playbook that is currently run. + +```bash + PLAY NAME OK CHANGED UNREACHABLE FAILED SKIPPED IGNORED IN PROGRESS TASK COUNT PROGRESS +0│Apache server installed 2 1 0 0 0 0 0 2 COMPLETE +``` -### Step 4 - Checking the Playbook -Now, let’s create a second playbook for post-configuration checks, named `system_checks.yml`: +If you notice, prior to the play name `Apache server installed`, you'll see a `0`. By pressing the `0` key on your keyboard, you will be provided a new window view displaying the different tasks that ran for the playbook completion. In this example, those tasks included the "Gathering Facts" and "Install Apache". The "Gathering Facts" is a built-in task that runs automatically at the beginning of each play. It collects information about the managed nodes. Exercises later on will cover this in more detail. The "Install Apache" was the task created within the `apache.yml` file that installed `httpd`. +The display should look something like this: + +```bash + RESULT HOST NUMBER CHANGED TASK TASK ACTION DURATION +0│OK node1 0 False Gathering Facts gather_facts 1s +1│OK node1 1 True Install Apache dnf 4s +``` + +Taking a closer look, you'll notice that each task is associated with a number. Task 1, "Install Apache", had a change and used the `dnf` module. In this case, the change is the installation of Apache (`httpd` package) on the host `node1`. + +By pressing `0` or `1` on your keyboard, you can see further details of the task being run. If a more traditional output view is desired, type `:st` within the text user interface. + +Once you've completed, reviewing your Ansible playbook, you can exit out of the TUI via the Esc key on your keyboard. + +> **Tip** +> +> The Esc key only takes you back to the previous screen. Once at the main overview screen an additional Esc key will take you back to the terminal window. + + +Once the playbook has completed, connect to `node1` via SSH to make sure Apache has been installed: + +```bash +[student@ansible-1 ansible-files]$ ssh node1 +Last login: Wed May 15 14:03:45 2019 from 44.55.66.77 +Managed by Ansible +``` + +Use the command `rpm -qi httpd` to verify httpd is installed: + +```bash +[ec2-user@node1 ~]$ rpm -qi httpd +Name : httpd +Version : 2.4.37 +[...] +``` + +Log out of `node1` with the command `exit` so that you are back on the control host and verify the installed package with an Ansible playbook labeled `package.yml` + +{% raw %} ```yaml --- -- name: System Configuration Checks +- name: Check packages hosts: node1 become: true + vars: + package: "httpd" + tasks: - - name: Check user existence - ansible.builtin.command: - cmd: id myuser - register: user_check - - - name: Report user status + - name: Gather the package facts + ansible.builtin.package_facts: + manager: auto + + - name: Check whether a {{ package }} is installed ansible.builtin.debug: - msg: "User 'myuser' exists." - when: user_check.rc == 0 + msg: "{{ package }} {{ ansible_facts.packages[ package ][0].version }} is installed!" + when: "package in ansible_facts.packages" + +``` +{% endraw %} + + +```bash +[student@ansible-1 ~]$ ansible-navigator run package.yml -m stdout +``` + +```bash + +PLAY [Check packages] ********************************************************** + +TASK [Gathering Facts] ********************************************************* +ok: [ansible] + +TASK [Gather the package facts] ************************************************ +ok: [ansible] + +TASK [Check whether a httpd is installed] ************************************* +ok: [ansible] => { + "msg": "httpd 2.4.37 is installed!" +} + +PLAY RECAP ********************************************************************* +ansible : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +``` + +Run the the `ansible-navigator run apache.yml` playbook for a second time, and compare the output. The output "CHANGED" now shows `0` instead of `1` and the color changed from yellow to green. This makes it easier to spot when changes have occured when running the Ansible playbook. + +### Step 4 - Extend your Playbook: Start & Enable Apache + +The next part of the Ansible playbook makes sure the Apache application is enabled and started on `node1`. + +On the control host, as your student user, edit the file `~/ansible-files/apache.yml` to add a second task using the `service` module. The Playbook should now look like this: + +```yaml +--- +- name: Apache server installed + hosts: node1 + become: true + tasks: + + - name: Install Apache + ansible.builtin.dnf: + name: httpd + + - name: Apache enabled and running + ansible.builtin.service: + name: httpd + enabled: true + state: started ``` -Run the checks playbook: +What exactly did we do? + +* a second task named "Apache enabled and running" is created +* a module is specified (`service`) +* The module `service` takes the name of the service (`httpd`), if it should be permanently set (`enabled`), and its current state (`started`) + + +Thus with the second task we make sure the Apache server is indeed running on the target machine. Run your extended Playbook: ```bash -[student@ansible-1 lab_inventory]$ ansible-navigator run system_checks.yml -m stdout +[student@ansible-1 ~]$ ansible-navigator run apache.yml +``` + +Notice in the output, we see the play had `1` "CHANGED" shown in yellow and if we press `0` to enter the play output, you can see that task 2, "Apache enabled and running", was the task that incorporated the latest change by the "CHANGED" value being set to True and highlighted in yellow. + + +* Run the playbook a second time using `ansible-navigator` to get used to the change in the output. + +* Use an Ansible playbook labeled service_state.yml to make sure the Apache (httpd) service is running on `node1`, e.g. with: `systemctl status httpd`. + +{% raw %} +```yaml +--- +- name: Check Status + hosts: node1 + become: true + vars: + package: "httpd" + + tasks: + - name: Check status of {{ package }} service + ansible.builtin.service_facts: + register: service_state + + - ansible.builtin.debug: + var: service_state.ansible_facts.services["{{ package }}.service"].state +``` + +```bash +{% endraw %} + +[student@ansible-1 ~]$ ansible-navigator run service_state.yml ``` -Review the output to ensure the user creation was successful. +### Step 5 - Extend your Playbook: Create an web.html + +Check that the tasks were executed correctly and Apache is accepting connections: Make an HTTP request using Ansible’s `uri` module in a playbook named check_httpd.yml from the control node to `node1`. + +{% raw %} +```yaml +--- +- name: Check URL + hosts: control + vars: + node: "node1" + + tasks: + - name: Check that you can connect (GET) to a page and it returns a status 200 + ansible.builtin.uri: + url: "http://{{ node }}" + +``` +{% endraw %} + +> **Warning** +> +> **Expect a lot of red lines and a 403 status\!** + +```bash +[student@ansible-1 ~]$ ansible-navigator run check_httpd.yml -m stdout +``` + +There are a lot of red lines and an error: As long as there is not at least an `web.html` file to be served by Apache, it will throw an ugly "HTTP Error 403: Forbidden" status and Ansible will report an error. + +So why not use Ansible to deploy a simple `web.html` file? On the ansible control host, as the `student` user, create the directory `files` to hold file resources in `~/ansible-files/`: + +```bash +[student@ansible-1 ansible-files]$ mkdir files +``` + +Then create the file `~/ansible-files/files/web.html` on the control node: + +```html + +

Apache is running fine

+ +``` + +In a previous example, you used Ansible’s `copy` module to write text supplied on the command line into a file. Now you’ll use the module in your playbook to copy a file. + +On the control node as your student user edit the file `~/ansible-files/apache.yml` and add a new task utilizing the `copy` module. It should now look like this: + +```yaml +--- +- name: Apache server installed + hosts: node1 + become: true + tasks: + + - name: Install Apache + ansible.builtin.dnf: + name: httpd + + - name: Apache enabled and running + ansible.builtin.service: + name: httpd + enabled: true + state: started + + - name: Copy index.html + ansible.builtin.copy: + src: web.html + dest: /var/www/html/index.html + mode: '644' +``` + +What does this new copy task do? The new task uses the `copy` module and defines the source and destination options for the copy operation as parameters. + +Run your extended Playbook: + +```bash +[student@ansible-1 ansible-files]$ ansible-navigator run apache.yml -m stdout +``` + +* Have a good look at the output, notice the changes of "CHANGED" and the tasks associated with that change. + +* Run the Ansible playbook check_httpd.yml using the "uri" module from above again to test Apache. The command should now return a friendly green "status: 200" line, amongst other information. + +### Step 6 - Practice: Apply to Multiple Host + +While the above, shows the simplicity of applying changes to a particular host. What about if you want to set changes to many hosts? This is where you'll notice the real power of Ansible as it applies the same set of tasks reliably to many hosts. + +* So what about changing the apache.yml Playbook to run on `node1` **and** `node2` **and** `node3`? + +As you might remember, the inventory lists all nodes as members of the group `web`: + +```ini +[web] +node1 ansible_host=11.22.33.44 +node2 ansible_host=22.33.44.55 +node3 ansible_host=33.44.55.66 +``` + +> **Tip** +> +> The IP addresses shown here are just examples, your nodes will have different IP addresses. + +Change the playbook `hosts` parameter to point to `web` instead of `node1`: + +```yaml +--- +- name: Apache server installed + hosts: web + become: true + tasks: + + - name: Install Apache + ansible.builtin.dnf: + name: httpd + + - name: Apache enabled and running + ansible.builtin.service: + name: httpd + enabled: true + state: started + + - name: Copy index.html + ansible.builtin.copy: + src: web.html + dest: /var/www/html/index.html + mode: '644' + +``` + +Now run the playbook: + +```bash +[student@ansible-1 ansible-files]$ ansible-navigator run apache.yml -m stdout +``` + +Verify if Apache is now running on all web servers (node1, node2, node3). All output should be green. + +--- +**Navigation** +
+{% if page.url contains 'ansible_rhel_90' %} +[Previous Exercise](../2-thebasics) - [Next Exercise](../4-variables) +{% else %} +[Previous Exercise](../1.2-thebasics) - [Next Exercise](../1.4-variables) +{% endif %} +

+[Click here to return to the Ansible for Red Hat Enterprise Linux Workshop](../README.md#section-1---ansible-engine-exercises) diff --git a/exercises/ansible_rhel/1.4-variables/README.md b/exercises/ansible_rhel/1.4-variables/README.md index 6134e294f..83591f537 100644 --- a/exercises/ansible_rhel/1.4-variables/README.md +++ b/exercises/ansible_rhel/1.4-variables/README.md @@ -5,352 +5,150 @@ ## Table of Contents -* [Objective](#objective) -* [Guide](#guide) -* [Intro to Variables](#intro-to-variables) -* [Step 1 - Create Variable Files](#step-1---create-variable-files) -* [Step 2 - Create index.html Files](#step-2---create-indexhtml-files) -* [Step 3 - Create the Playbook](#step-3---create-the-playbook) -* [Step 4 - Test the Result](#step-4---test-the-result) -* [Step 5 - Ansible Facts](#step-5---ansible-facts) -* [Step 6 - Challenge Lab: Facts](#step-6---challenge-lab-facts) -* [Step 7 - Using Facts in Playbooks](#step-7---using-facts-in-playbooks) +- [Workshop Exercise - Using Variables](##workshop-exercise---using-variables) + - [Objective](#objective) + - [Guide](#guide) + - [Step 1 - Understanding Variables](#step-1---understanding-variables) + - [Step 2 - Variable Syntax and Creation](#step-2---variable-syntax-and-creation) + - [Step 3 - Running the Modified Playbook](#step-3---running-the-modified-playbook) + - [Step 4 - Advanced Variable Usage in Checks Playbook](#step-4---advanced-variable-usage-in-checks-playbook) -## Objective - -Ansible supports variables to store values that can be used in Ansible playbooks. Variables can be defined in a variety of places and have a clear precedence. Ansible substitutes the variable with its value when a task is executed. -This exercise covers variables, specifically - -* How to use variable delimiters `{{` and `}}` -* What `host_vars` and `group_vars` are and when to use them -* How to use `ansible_facts` -* How to use the `debug` module to print variables to the console window +## Objective +Extending our playbooks from Exercise 1.3, the focus turns to the creation and usage of variables in Ansible. You'll learn the syntax for defining and using variables, an essential skill for creating dynamic and adaptable playbooks. ## Guide +Variables in Ansible are powerful tools for making your playbooks flexible and reusable. They allow you to store and reuse values, making your playbooks more dynamic and adaptable. -### Intro to Variables - -Variables are referenced in Ansible Playbooks by placing the variable name in double curly braces: - - - -```yaml -Here comes a variable {{ variable1 }} -``` - - - -Variables and their values can be defined in various places: the inventory, additional files, on the command line, etc. - -The recommended practice to provide variables in the inventory is to define them in files located in two directories named `host_vars` and `group_vars`: - -* To define variables for a group "servers", a YAML file named `group_vars/servers.yml` with the variable definitions is created. -* To define variables specifically for a host `node1`, the file `host_vars/node1.yml` with the variable definitions is created. - -> **Tip** -> -> Host variables take precedence over group variables (more about precedence can be found in the [docs](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable)). - -### Step 1 - Create Variable Files - -For understanding and practice let’s do a lab. Following up on the theme "Let’s build a web server. Or two. Or even more…​", you will change the `index.html` to show the development environment (dev/prod) a server is deployed in. - -On the ansible control host, as the `student` user, create the directories to hold the variable definitions in `~/ansible-files/`: - -```bash -[student@ansible-1 ansible-files]$ mkdir host_vars group_vars -``` - -Now create two files containing variable definitions. We’ll define a variable named `stage` which will point to different environments, `dev` or `prod`: - -* Create the file `~/ansible-files/group_vars/web.yml` with this content: - -```yaml ---- -stage: dev -``` - -* Create the file `~/ansible-files/host_vars/node2.yml` with this content: - -```yaml ---- -stage: prod -``` - -What is this about? - -* For all servers in the `web` group the variable `stage` with value `dev` is defined. So as default we flag them as members of the dev environment. -* For server `node2` this is overridden and the host is flagged as a production server. - -### Step 2 - Create web.html Files - -Now create two files in `~/ansible-files/files/`: - -One called `prod_web.html` with the following content: - -```html - -

This is a production webserver, take care!

- -``` - -And the other called `dev_web.html` with the following content: - -```html - -

This is a development webserver, have fun!

- -``` - -### Step 3 - Create the Playbook +### Step 1 - Understanding Variables +A variable in Ansible is a named representation of some data. Variables can contain simple values like strings and numbers, or more complex data like lists and dictionaries. -Now you need a Playbook that copies the prod or dev `web.html` file - according to the "stage" variable. +### Step 2 - Variable Syntax and Creation +The creation and usage of variables involve a specific syntax: -Create a new Playbook called `deploy_index_html.yml` in the `~/ansible-files/` directory. +1. Defining Variables: Variables are defined in the `vars` section of a playbook or in separate files for larger projects. +2. Variable Naming: Variable names should be descriptive and adhere to rules such as: + * Starting with a letter or underscore. + * Containing only letters, numbers, and underscores. +3. Using Variables: Variables are referenced in tasks using the double curly braces in quotes `"{{ variable_name }}"`. This syntax tells Ansible to replace it with the variable's value at runtime. -> **Tip** -> -> Note how the variable "stage" is used in the name of the file to copy. - - +Update the `system_setup.yml` playbook to include and use a variable: ```yaml --- -- name: Copy web.html - hosts: web +- name: Basic System Setup + hosts: node1 become: true + vars: + user_name: 'Roger' tasks: - - name: copy web.html - ansible.builtin.copy: - src: "{{ stage }}_web.html" - dest: /var/www/html/index.html -``` - - - -* Run the Playbook: - -```bash -[student@ansible-1 ansible-files]$ ansible-navigator run deploy_index_html.yml + - name: Update all security-related packages + ansible.builtin.dnf: + name: '*' + state: latest + security: true + + - name: Create a new user + ansible.builtin.user: + name: "{{ user_name }}" + state: present + create_home: true ``` -### Step 4 - Test the Result +Run this playbook with `ansible-navigator`. -The Ansible Playbook copies different files as index.html to the hosts, use `curl` to test it. +### Step 3 - Running the Modified Playbook -For node1: +Execute the updated playbook: ```bash -[student@ansible-1 ansible-files]$ curl http://node1 - -

This is a development webserver, have fun!

- +[student@ansible-1 lab_inventory]$ ansible-navigator run system_setup.yml -m stdout ``` -For node2: - -```bash -[student@ansible-1 ansible-files]$ curl http://node2 - -

This is a production webserver, take care!

- ``` +PLAY [Basic System Setup] ****************************************************** -For node3: - -```bash -[student@ansible-1 ansible-files]$ curl http://node3 - -

This is a development webserver, have fun!

- -``` - -> **Tip** -> -> If by now you think: There has to be a smarter way to change content in files…​ you are absolutely right. This lab was done to introduce variables, you are about to learn about templates in one of the next chapters. - -### Step 5 - Ansible Facts - -Ansible facts are variables that are automatically discovered by Ansible from a managed host. Remember the "Gathering Facts" task listed in the output of each `ansible-navigator` execution? At that moment the facts are gathered for each managed nodes. Facts can also be pulled by the `setup` module. They contain useful information stored into variables that administrators can reuse. - -To get an idea what facts Ansible collects by default, on your control node as your student user run the following playbook to get the setup details of `node1`: - -```yaml ---- -- name: Capture Setup - hosts: node1 - - tasks: +TASK [Gathering Facts] ********************************************************* +ok: [node1] - - name: Collect only facts returned by facter - ansible.builtin.setup: - gather_subset: - - 'all' - register: setup +TASK [Update all security-related packages] ************************************ +ok: [node1] - - ansible.builtin.debug: - var: setup -``` +TASK [Create a new user] ******************************************************* +changed: [node1] -```bash -[student@ansible-1 ansible-files]$ cd ~ -[student@ansible-1 ~]$ ansible-navigator run setup.yml -m stdout +PLAY RECAP ********************************************************************* +node1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ``` -This might be a bit too much, you can use filters to limit the output to certain facts, the expression is shell-style wildcard within your playbook. Create a playbook labeled `setup_filter.yml` as shown below. In this example, we filter to get the `eth0` facts as well as memory details of `node1`. +Notice how the updated playbook shows a status of changed in the Create a new user task. The user, ‘Roger’, specified within the vars section has been created. -```yaml ---- -- name: Capture Setup - hosts: node1 - - tasks: - - - name: Collect only specific facts - ansible.builtin.setup: - filter: - - 'ansible_eth0' - - 'ansible_*_mb' - register: setup - - - debug: - var: setup -``` +Verify the user creation via: ```bash -[student@ansible-1 ansible-files]$ ansible-navigator run setup_filter.yml -m stdout +[student@ansible-1 lab_inventory]$ ssh node1 id Roger ``` -### Step 6 - Challenge Lab: Facts +### Step 4 - Advanced Variable Usage in Checks Playbook +Enhance the `system_checks.yml` playbook to check for the ‘Roger’ user within the system using the `register` variable and `when` conditional statement. -* Try to find and print the distribution (Red Hat) of your managed hosts using a playbook. +The register keyword in Ansible is used to capture the output of a task and save it as a variable. -> **Tip** -> -> Use the wildcard to find the fact within your filter, then apply a filter to only print this fact. -> **Warning** -> -> **Solution below\!** +Update the `system_checks.yml` playbook: ```yaml --- -- name: Capture Setup +- name: System Configuration Checks hosts: node1 - + become: true + vars: + user_name: 'Roger' tasks: - - - name: Collect only specific facts - ansible.builtin.setup: - filter: - - '*distribution' - register: setup - - - ansible.builtin.debug: - var: setup + - name: Check user existence + ansible.builtin.command: + cmd: "id {{ user_name }}" + register: user_check + + - name: Report user status + ansible.builtin.debug: + msg: "User {{ user_name }} exists." + when: user_check.rc == 0 ``` -With the wildcard in place, the output shows: +Playbook Details: -```bash +* `register: user_check:` This captures the output of the id command into the variable user_check. +* `when: user_check.rc == 0:` This line is a conditional statement. It checks if the return code (rc) of the previous task (stored in user_check) is 0, indicating success. The debug message will only be displayed if this condition is met. -TASK [debug] ******************************************************************* -ok: [ansible] => { - "setup": { - "ansible_facts": { - "ansible_distribution": "RedHat" - }, - "changed": false, - "failed": false - } -} -``` - -With this we can conclude the variable we are looking for is labeled `ansible_distribution`. +This setup provides a practical example of how variables can be used to control the flow of tasks based on the outcomes of previous steps. -Then we can update the playbook to be explicit in its findings and change the following line: -```yaml -filter: -- '*distribution' -``` - -to: - -```yaml -filter: -- 'ansible_distribution' -``` +Run the checks playbook: ```bash -[student@ansible-1 ansible-files]$ ansible-navigator run setup_filter.yml -m stdout +[student@ansible-1 lab_inventory]$ ansible-navigator run system_checks.yml -m stdout ``` -### Step 7 - Using Facts in Playbooks - -Facts can be used in a Playbook like variables, using the proper naming, of course. Create this Playbook as `facts.yml` in the `~/ansible-files/` directory: +Output: - - -```yaml ---- -- name: Output facts within a playbook - hosts: all - tasks: - - name: Prints Ansible facts - ansible.builtin.debug: - msg: The default IPv4 address of {{ ansible_fqdn }} is {{ ansible_default_ipv4.address }} ``` - - - -> **Tip** -> -> The "debug" module is handy for e.g. debugging variables or expressions. - -Execute it to see how the facts are printed: - -```bash -[student@ansible-1 ansible-files]$ ansible-navigator run facts.yml -``` - -Within the text user interface (TUI) window, type `:st` to capture the following output: - -```bash -PLAY [Output facts within a playbook] ****************************************** +PLAY [System Configuration Checks] ********************************************* TASK [Gathering Facts] ********************************************************* -ok: [node3] -ok: [node2] ok: [node1] -ok: [ansible-1] - -TASK [Prints Ansible facts] **************************************************** -ok: [node1] => - msg: The default IPv4 address of node1 is 172.16.190.143 -ok: [node2] => - msg: The default IPv4 address of node2 is 172.16.30.170 -ok: [node3] => - msg: The default IPv4 address of node3 is 172.16.140.196 -ok: [ansible-1] => - msg: The default IPv4 address of ansible is 172.16.2.10 + +TASK [Check user existence] **************************************************** +changed: [node1] + +TASK [Report user status] ****************************************************** +ok: [node1] => { + "msg": "User Roger exists." +} PLAY RECAP ********************************************************************* -ansible-1 : ok=2 changed=0 unreachable=0 failed=0 -node1 : ok=2 changed=0 unreachable=0 failed=0 -node2 : ok=2 changed=0 unreachable=0 failed=0 -node3 : ok=2 changed=0 unreachable=0 failed=0 +node1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ``` ---- -**Navigation** -
- -{% if page.url contains 'ansible_rhel_90' %} -[Previous Exercise](../3-playbook) - [Next Exercise](../5-surveys) -{% else %} -[Previous Exercise](../1.3-playbook) - [Next Exercise](../1.5-handlers) -{% endif %} -

-[Click here to return to the Ansible for Red Hat Enterprise Linux Workshop](../README.md) +Review the output to confirm the user existence check is correctly using the variable and conditional logic. +