Manipulate Files with Oracle Linux Automation Engine
Introduction
Oracle Linux Automation Engine automates the execution of playbooks on remote hosts deployed within your organization, often including the ability to create files and directories as part of the overall setup process as part of configuring a system.
This tutorial shows how to create and delete files and directories on your remote hosts.
Objectives
In this lab, you'll learn about writing and running a playbook that:
- Creates an empty file
- Creates a file with content
- Creates multiple files in a single task
- Sets file permissions
- Creates a directory
- Removes files and directories
What Do You Need?
A minimum of two Oracle Linux systems with the following configuration:
- a non-root user with
sudo
permissions - ssh keypair for the non-root user
- the ability to SSH from one host (control-node) to the other (host) using passwordless SSH login
- a non-root user with
Additional Information
Note: When using the free lab environment, see Oracle Linux Lab Basics for connection and other usage instructions.
Information: The free lab environment deploys an Oracle Linux on the provided nodes. This deployment takes approximately 5 minutes to finish after launch. Therefore, you might want to step away while this runs and then return to complete the lab.
Before proceeding: If you are unfamiliar with running playbooks, check out our introduction tutorial, Write a Playbook with Oracle Linux Automation Engine . This tutorial follows on from this tutorial, Enable the EPEL Repository with Oracle Linux Automation Engine .
Verify the Inventory
From a terminal on the Oracle Linux Automation Engine Control Node:
Open a terminal and connect via SSH to the ol-control-node.
ssh oracle@<ip_address_of_ol-control-node>
Change to the working directory.
cd ~/ol-setup-playbook
The working directory stores the existing playbook and other files from the previous tutorial associated with this project.
Note: This directory already exists when using the free lab environment or if continuing from the steps provided in the base tutorial. If the directory does not exist, complete the base tutorial steps at a minimum before proceeding.
Test the connection with the ad-hoc
ping
command.ansible ol-node01 -i inventory -m ping -u opc
The command connects successfully, reporting back SUCCESS with the ping key having a value of pong.
Update the Playbook
Add the following to the existing playbook file.
cat << EOF | tee -a setup-playbook.yml > /dev/null - name: Create an example directory (if it does not already exist) ansible.builtin.file: path: "/home/{{ username }}/example" state: directory mode: '0755' become_user: "{{ username }}" - name: Create an empty file ansible.builtin.file: path: "/home/{{ username }}/example/test.txt" state: touch # mode: u=rw,g=r,o=r become_user: "{{ username }}" EOF
This playbook adds two tasks within the defined play. The first creates a new directory called
example
in the remote user's $HOME directory, and the second creates an empty file calledtest.txt
in the newly createdexample
directory.While a playbook aims to be self-documenting, the information below will explain a few items further.
state:
: Theansible.builtin.file
module supports the following parameters: "absent", "directory", "file", "hard", "link" and "touch". These tasks use "directory", which creates the directory path if not already present, and "touch", which creates an empty file if not already present.mode
: Sets the filesystem permissions for the object created. When using the command line, you can use either octal ('0755') or symbolic (u=rw,g=r,o=r) modes (equivalent to 0644 in octal mode). The system default mode is applied if you omit the mode: parameter.become_user: "{{ username }}"
: Uses the Oracle Linux Automation Engine privilege escalation functionality to execute a task. In previous tutorials, we introduced thebecome
privilege escalation functionality to run a task as root. This example illustrates how to use similar functionality to execute a task as another user. We set the user as the variable"{{ username }}"
, which we predefine as the oracle user in thevars/defaults.yml
file. Hence, this task runs as the oracle user and inherits that user's defaults.
Run the Playbook
Run the playbook to perform the additional tasks.
ansible-playbook -i inventory setup-playbook.yml -u opc
The playbook finishes successfully, showing a few
WARNINGS
that we can ignore and reporting the tasks with anok
orchanged
status.Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ansible-playbook -i inventory setup-playbook.yml -u opc PLAY [Playbook to setup Oracle Linux] ********************************************************** TASK [Gathering Facts] ************************************************************************* [WARNING]: Platform linux on host ol-node01 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible- core/2.15/reference_appendices/interpreter_discovery.html for more information. ok: [ol-node01] TASK [Add user account with access to sudo] **************************************************** [DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. Install the passlib library for continued encryption functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg. ok: [ol-node01] TASK [Set authorized key for user using local public key file] ********************************* changed: [ol-node01] TASK [Install additional packages] ************************************************************* ok: [ol-node01] TASK [Add the EPEL repository] ***************************************************************** ok: [ol-node01] TASK [Install the htop utility package] ******************************************************** ok: [ol-node01] TASK [Create an example directory (if it does not already exist)] ****************************** [WARNING]: Module remote_tmp /home/oracle/.ansible/tmp did not exist and was created with a mode of 0700, this may cause issues when running as another user. To avoid this, create the remote_tmp dir with the correct permissions manually changed: [ol-node01] TASK [Create an empty file] ******************************************************************** changed: [ol-node01] PLAY RECAP ************************************************************************************* ol-node01 : ok=8 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Confirm The New Directory and File Exist
Use SSH to get a directory listing on the remote host.
ssh oracle@<ip_address_of_ol-node> ls -al example
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ssh oracle@130.162.213.42 ls -al example total 0 drwxr-xr-x. 2 oracle oracle 22 Feb 2 14:47 . drwx------. 5 oracle oracle 105 Feb 2 14:47 .. -rw-r--r--. 1 oracle oracle 0 Feb 2 14:47 test.txt
The output confirms the creation of a new directory and the empty file. Notice that the
test.txt
file has zero bytes, and theoracle
user owns it.
Create Multiple Files
During the setup of a system, you may need to create several files within a directory. Rather than using individual tasks, you can create several files in a directory as an atomic operation. Here is one approach to achieving this.
Add the following tasks to the playbook file.
cat << EOF | tee -a setup-playbook.yml > /dev/null - name: Add multiple files ansible.builtin.file: path: "/home/{{ username }}/example/{{ item }}" state: touch with_items: - file01.txt - file02.txt - file03.txt - file04.txt become_user: "{{ username }}" EOF
This task uses a loop to create several empty files from a list.
path:
: Defines the location on the remote system to write the files. Oracle Linux Automation Engine replaces the{{ item }}
variable with eachitem
from thewith_items
parameter during runtime.with_items:
: This parameter indicates the list ofitems
to loop over during the running of this task. While this example uses only four file names, the list can be as long as you need.
Run the playbook to perform the additional tasks.
ansible-playbook -i inventory setup-playbook.yml -u opc
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ansible-playbook -i inventory setup-playbook.yml -u opc PLAY [Playbook to setup Oracle Linux] ******************************************************************** TASK [Gathering Facts] *********************************************************************************** [WARNING]: Platform linux on host ol-node01 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.15/reference_appendices/interpreter_discovery.html for more information. ok: [ol-node01] ... TASK [Create an empty file] ****************************************************************************** changed: [ol-node01] TASK [Create an example directory (if it does not already exist)] **************************************** changed: [ol-node01] => (item=file01.txt) changed: [ol-node01] => (item=file02.txt) changed: [ol-node01] => (item=file03.txt) changed: [ol-node01] => (item=file04.txt) PLAY RECAP *********************************************************************************************** ol-node01 : ok=9 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Confirm The New Files Exist
Use SSH again to confirm the files exist.
ssh oracle@<ip_address_of_ol-node> ls -al example
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ssh oracle@130.162.213.42 ls -al example total 0 drwxr-xr-x. 2 oracle oracle 94 Feb 2 15:20 . drwx------. 5 oracle oracle 105 Feb 2 14:47 .. -rw-r--r--. 1 oracle oracle 0 Feb 2 15:20 file01.txt -rw-r--r--. 1 oracle oracle 0 Feb 2 15:20 file02.txt -rw-r--r--. 1 oracle oracle 0 Feb 2 15:20 file03.txt -rw-r--r--. 1 oracle oracle 0 Feb 2 15:20 file04.txt -rw-r--r--. 1 oracle oracle 0 Feb 2 15:20 test.txt
Add Content To a File
While creating a file is helpful, an empty file serves little purpose. These next few tasks use the ansible.builtin.lineinfile module to insert individual lines and the ansible.builtin.blockinfile module to handle a block of text.
Add a Single Line to a File
Update the playbook file with the new tasks.
cat << 'EOF' | tee -a setup-playbook.yml > /dev/null - name: Insert some text into the test.txt file ansible.builtin.lineinfile: path: "/home/{{ username }}/example/test.txt" line: This is a test create: True owner: "{{ username }}" group: "{{ username }}" mode: '0644' - name: Add an extra line, after the line just inserted ansible.builtin.lineinfile: path: "/home/{{ username }}/example/test.txt" regexp: '^a test' insertafter: 'This is a test' line: This is some updated text that was added later. create: True owner: "{{ username }}" group: "{{ username }}" mode: '0644' - name: Get the contents of the test.txt file ansible.builtin.command: cat ~/example/test.txt register: command_output become_user: "{{ username }}" - name: Print the results of the cat command ansible.builtin.debug: msg: "{{ command_output }}" - name: Print only the lines added to the text file ansible.builtin.debug: msg: "{{ command_output.stdout_lines }}" EOF
The first two tasks use the ansible.builtin.lineinfile to add and update single lines in the file.
The remaining tasks show a method to confirm the changes. The playbook first uses the ansible.builtin.command to output the content of the updated file to stdout and saves it to memory in a
register
variable. Then it uses the ansible.builtin.debug module to print that content to the screen as part of the playbook output. The second debug task shows a way to limit the output to only a particular part of the JSON output from the previous task.
Run the playbook to perform the additional tasks.
ansible-playbook -i inventory setup-playbook.yml -u opc
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ansible-playbook -i inventory setup-playbook.yml -u opc PLAY [Playbook to setup Oracle Linux] ******************************************************************** TASK [Gathering Facts] *********************************************************************************** [WARNING]: Platform linux on host ol-node01 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.15/reference_appendices/interpreter_discovery.html for more information. ok: [ol-node01] ... TASK [Insert some text into the 'test.txt' file] ***************************************************************************** ok: [ol-node01] TASK [Add an extra line, after the line just inserted] *********************************************************************** ok: [ol-node01] TASK [Get the contents of the 'test.txt' file] ******************************************** changed: [ol-node01] TASK [Print the command output] ************************************************************************************* ok: [ol-node01] => { "msg": { "changed": true, "cmd": [ "cat", "~/example/test.txt" ], "delta": "0:00:00.001678", "end": "2024-01-30 13:43:50.209312", "failed": false, "msg": "", "rc": 0, "start": "2024-01-30 13:43:50.207634", "stderr": "", "stderr_lines": [], "stdout": "This is a test\nThis is some updated text that was added later.", "stdout_lines": [ "This is a test", "This is some updated text that was added later." ] } } TASK [Print only the lines added to the text file] *************************************************************************** ok: [ol-node01] => { "msg": [ "This is a test", "This is some updated text that was added later." ] } PLAY RECAP ******************************************************************************************************************* ol-node01 : ok=14 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Add Multiple Lines to a File
Adding a large text block one line at a time is slow and inefficient. Oracle Linux Automation Engine provides multiple ways to achieve these tasks based on your requirements. The ansible.builtin.blockinfile module allows the insert, update, and removal of multiple lines of text with custom markers. These markers enable easy identification of the text and ensure idempotency. The idempotency of Oracle Linux Automation Engine allows running the playbook repeatedly, yielding the same results.
The second option uses the ansible.builtin.copy module. This module's primary purpose is to copy files from one location to another, but it can also create a new file and insert text into it in a single operation.
Update the playbook with the new tasks.
cat << EOF | tee -a setup-playbook.yml > /dev/null - name: Add two lines into test.txt ansible.builtin.blockinfile: path: "/home/{{ username }}/example/test.txt" insertafter: 'This is some updated text that was added later.' block: | "Welcome to {{ ansible_hostname }}" "Last updated on {{ ansible_date_time.iso8601 }}" - name: Create a new file and insert content into it ansible.builtin.copy: content: | === The text below was added by Oracle Linux Automation Engine ========== Hello from the ansible.builtin.copy module This is an example of how to insert more than one line into a text file You can insert more lines if you want === The text above was added by Oracle Linux Automation Engine =========== dest: "/home/{{ username }}/example/testing.txt" mode: '0644' become_user: "{{ username }}" EOF
Run the playbook to perform the additional tasks.
ansible-playbook -i inventory setup-playbook.yml -u opc
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ansible-playbook -i inventory setup-playbook.yml -u opc PLAY [Playbook to setup Oracle Linux] ******************************************************************** TASK [Gathering Facts] *********************************************************************************** [WARNING]: Platform linux on host ol-node01 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.15/reference_appendices/interpreter_discovery.html for more information. ok: [ol-node01] ... TASK [Add two lines into test.txt] ******************************************************************************************* changed: [ol-node01] TASK [Create a new file and insert content into it] ************************************************************************** changed: [ol-node01] PLAY RECAP ******************************************************************************************************************* ol-node01 : ok=16 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Confirm The New Lines Exist
Confirm two new lines are present in
test.txt
.ssh oracle@<ip_address_of_ol-node> cat example/test.txt
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ssh oracle@130.162.213.42 cat example/test.txt This is a test This is some updated text that was added later. # BEGIN ANSIBLE MANAGED BLOCK "Welcome to ol-node01" "Last updated on 2024-01-30T14:05:58Z" # END ANSIBLE MANAGED BLOCK
The output shows the added content and interprets the task's variable values based on the automatic fact gathering done by the playbook against the remote host during runtime.
Confirm creating the
testing.txt
file and the added content.ssh oracle@<ip_address_of_ol-node> cat example/testing.txt
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ssh oracle@130.162.213.42 cat example/testing.txt === The text below was added by Oracle Linux Automation Engine ========== Hello from the ansible.builtin.copy module This is an example of how to insert more than one line into a text file You can insert more lines if you want === The text above was added by Oracle Linux Automation Engine ===========
Delete Files and Directories
Besides creating files and directories and adding text, Oracle Linux Automation Engine can also delete these items. Let's remove the directory and all the files this playbook creates and leave the system in the same state as when we started. These tasks first use the state:
parameter set to absent to remove the list of files and then the directory. The second task handles removing the files in a single step along with the directory. Still, we include the first task for additional demonstration of the Oracle Linux Automation Engine features.
Add the following to the playbook file.
cat << EOF | tee -a setup-playbook.yml > /dev/null - name: Delete multiple files ansible.builtin.file: path: '{{ item }}' state: absent with_items: - "/home/{{ username }}/example/test.txt" - "/home/{{ username }}/example/file01.txt" - "/home/{{ username }}/example/file02.txt" - "/home/{{ username }}/example/file03.txt" - "/home/{{ username }}/example/file04.txt" - "/home/{{ username }}/example/testing.txt" - name: Recursively remove directory ansible.builtin.file: path: "/home/{{ username }}/example" state: absent EOF
Note: It is possible to recursively delete a directory (including any contents) using the second task shown. The first task, deleting the files individually, is present in case you only want to delete files. If you want to delete both files and files and directories, the second task is more efficient.
Run the playbook to perform the additional tasks.
ansible-playbook -i inventory setup-playbook.yml -u opc
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ansible-playbook -i inventory setup-playbook.yml -u opc PLAY [Playbook to setup Oracle Linux] ******************************************************************** TASK [Gathering Facts] *********************************************************************************** [WARNING]: Platform linux on host ol-node01 is using the discovered Python interpreter at /usr/bin/python3.6, but future installation of another Python interpreter could change the meaning of that path. See https://docs.ansible.com/ansible-core/2.15/reference_appendices/interpreter_discovery.html for more information. ok: [ol-node01] ... TASK [Delete multiple files] ********************************************************************************************* changed: [ol-node01] => (item=/home/oracle/example/test.txt) changed: [ol-node01] => (item=/home/oracle/example/file01.txt) changed: [ol-node01] => (item=/home/oracle/example/file02.txt) changed: [ol-node01] => (item=/home/oracle/example/file03.txt) changed: [ol-node01] => (item=/home/oracle/example/file04.txt) changed: [ol-node01] => (item=/home/oracle/example/testing.txt) TASK [Recursively remove directory] ************************************************************************************** changed: [ol-node01] PLAY RECAP *************************************************************************************************************** ol-node01 : ok=18 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Confirm Directory and Files Deletion
Verify the removal of the
example
directory.ssh oracle@<ip_address_of_ol-node> ls
Example Output:
[oracle@ol-control-node ol-setup-playbook]$ ssh oracle@130.162.213.42 ls [oracle@ol-control-node ol-setup-playbook]$
The lack of output confirms the
example
directory and the files created during this tutorial are gone.
Summary
Congratulations on making it this far. This tutorial introduced a flavor of the approaches Oracle Linux Automation Engine makes available to create, update, and delete files and directories. The tutorial also showed how to use Oracle Linux Automation Engine's ansible.builtin.debug
module to return information to the terminal while executing a Playbook.
For More Information
Oracle Linux Automation Manager Documentation
Oracle Linux Automation Manager Training
Oracle Linux Training Station