Baeldung Pro – Ops – NPI EA (cat = Baeldung on Ops)
announcement - icon

Learn through the super-clean Baeldung Pro experience:

>> Membership and Baeldung Pro.

No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.

1. Introduction

Ansible is a simple and powerful IT automation tool. In this tutorial, we see different ways to create directories using Ansible. Moreover, we cover different use cases. This includes setting permissions, creating directory trees, deleting them, and others.

2. Overview

Before we move on with this tutorial, let’s set up a test environment. Also, we need to ensure some prerequisites:

  • Ansible installed on the controller machine
  • ssh connectivity between the controller and remote clients
  • An inventory file that lists the remote systems where we want to create directories

With the above requirements in place, we can start using Ansible to create directories.

3. File Module in Ansible

The file module in Ansible is a handy tool for handling files and directories. It has many options for creating, modifying, and deleting files and directories.

Also, we’ve got some commonly used parameters in the file module:

  • path: sets the directory or file path
  • state: defines the desired state (for example, directory to create a directory, absent to delete it)
  • mode: sets file or directory permissions using octal notation

Let’s start with some examples of handling directories with Ansible.

4. Creating a Directory

To create a simple directory, we use the state: directory parameter from the file module. Along with this, we set the path of the directory:

$ cat creating_directory.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Create a directory
      file:
        path: /opt/my_dir
        state: directory

Here’s the breakdown of the above playbook:

  • hosts: client1: applies the playbook to the host client1 listed in the inventory file
  • tasks: defines the list of tasks that Ansible will run
  • file: calls the file module for file-based operations
  • path: sets the directory path
  • state: directory: ensures the directory is created if it doesn’t exist

Notably, we’re creating directories in the system-owned /opt directory, which requires elevated privileges. Thus, we’ve got the become keyword in the above playbook. 

Many of the above options will repeat in the coming playbooks.

Next, we run the playbook:

$ ansible-playbook -i project_inventory.ini creating_directory.yml 
PLAY [client1] *******************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.29.21]
TASK [Create a directory] ********************************************************************
changed: [192.168.29.21]
PLAY RECAP *********
192.168.29.21              : ok=2    changed=1 ...
...

The option -i sets the inventory file to use. On the target host, we can see to confirm the changes:

$ ls /opt/
my_dir
...

As a result, the above playbook creates my_directory on the remote system.

5. Creating a Directory with Permissions

To set access permissions for the created directory, we use the mode parameter. For example, let’s create a directory with different read, write, and execute permissions for the owner, group, and others:

$ cat directory_permissions.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Adding access permissions
      file:
        path: /opt/secured_directory
        state: directory
        mode: '0750'

The above playbook has a new parameter mode. As a matter of fact, the mode value 0750 is in octal notation. This means the owner has read, write, and execute permissions. However, others can’t read, write, and execute the directory.

Next, we run the playbook:

$ ansible-playbook -i project_inventory.ini directory_permissions.yml 
PLAY [client1] ******...
TASK [Gathering Facts] ******...
ok: [192.168.29.21]
TASK [Adding access permissions] ******...
PLAY RECAP *******
192.168.29.21              : ok=2    changed=1 ...
...

Thus, we can see secure_directory is created with the right permissions. On the remote machine, we can check the set permission:

$ ls -ll /opt/
drwxr-x--- 2 root root 4096 Sep 17 04:01 secured_directory

We can also add owner and group rights from the playbook.

6. Creating a Directory Tree

To create multiple nested directories, we again use the file module. This is a tree-type structure. Again, we can do this by setting the path and state parameters.

Going ahead, we’ve got a new playbook for this use case:

$ cat directory_tree.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Create a directory tree
      file:
        path: /opt/parent/child/grandchild
        state: directory

The above task creates the whole directory tree. This also includes any missing parent directories. Thus, we get a nested structure with parent, child, and grandchild directories under /opt.

Next, we run the example playbook to see how to create nested directories:

$ ansible-playbook -i project_inventory.ini directory_tree.yml 
PLAY [client1] **********************************************
TASK [Gathering Facts] **************************************
ok: [192.168.29.21]
TASK [Create a directory tree] *****************************
changed: [192.168.29.21]
PLAY RECAP **********
192.168.29.21              : ok=2    changed=1 ...
...

We can now check the result on the remote machine:

$ tree /opt/parent/
/opt/parent/
└── child
    └── grandchild
2 directories, 0 files

As a result, we can see the tree command listing all the directories.

Sometimes a part of the directory tree does not exist (e.g., parent or child). In that case, the playbook will create that part. Thus, we get the whole path.

7. Creating Multiple Directories

We can also create multiple directories in a single task in Ansible. We can do this by using a loop. In this way, we can reduce redundancy in the playbook.

$ cat multiple_directories.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Create multiple directories
      file:
        path: "{{ item }}"
        state: directory
      with_items:
        - /opt/dir1
        - /opt/dir2
        - /opt/dir3

In the above playbook, the with_items parameter loops through the given paths.

Moving on, we run the playbook:

$ ansible-playbook -i project_inventory.ini multiple_directories.yml 
PLAY [client1] ****************...
TASK [Gathering Facts] *********...
ok: [192.168.29.21]
TASK [Create multiple directories] ********...
changed: [192.168.29.21] => (item=/opt/dir1)
changed: [192.168.29.21] => (item=/opt/dir2)
changed: [192.168.29.21] => (item=/opt/dir3)
PLAY RECAP **********
192.168.29.21              : ok=2    changed=1 ...
...

When we check on the remote machine, we see all the directories:

$ ls /opt/dir*
/opt/dir1:
/opt/dir2:
/opt/dir3:

Thus, the playbook loops over a list of directory paths and creates each one.

8. Creating a Timestamped Directory

A timestamped directory is useful for logging or backup purposes. For this, we can use the ansible_date_time variable. It dynamically creates directories with the current date and time.

Let’s write a new playbook:

$ cat timestamped_directory.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Adding a timestamp to directory name
      file:
        path: "/opt/backup_{{ ansible_date_time.date }}_{{ ansible_date_time.time }}"
        state: directory

When we run the playbook, we get a directory named with the current date and time:

$ ansible-playbook -i project_inventory.ini timestamped_directory.yml 
PLAY [client1] ************
TASK ****.....
ok: [192.168.29.21]
TASK [Adding a timestamp to directory name] ************ changed: [192.168.29.21] PLAY RECAP *************** 192.168.29.21 : ok=2 changed=1 ... ... 

Here, the ansible_date_time built-in variable dynamically fetches the current date and time. As a result, the directory name is time-stamped with the current date and time.

We can check the directory name on the remote machine:

$ ls /opt/
backup_2024-09-17_04:28:44

This way of naming is useful when we need directories for special purposes. For example, backup directories need to have a unique, time-stamped name. In the same manner, we can use it for logging work.

9. Deleting a Directory

The file module also supports deleting directories. By setting the state parameter to absent, we can remove a directory and all its contents.

Let’s have a playbook for this case:

$ cat deleting_directory.yml
---
- hosts: client1
  become: true
  tasks:
    - name: Remove a directory
      file:
        path: /opt/dir1
        state: absent

When we run above the playbook, the given directory is removed:

$ ansible-playbook -i project_inventory.ini deleting_directory.yml 
PLAY [client1] *******************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.29.21]
TASK [Remove a directory] ********************************************************************
changed: [192.168.29.21]
PLAY RECAP ***********************************************************************************
192.168.29.21              : ok=2    changed=1 ...
...

Again, we can use the ls command to check changes on the remote host.

This is useful when we want to clean up old or unused directories in a system. Notably, all the files inside them are also removed.

Sometimes, the directory to be removed may not exist on the remote machine. In this case, the playbook runs without any error. However, the status of the changed flag in the playbook output doesn’t change.

10. Creating a Directory Using the install Command

Another option to create a directory is by using the install command. For this, we can use the command or shell module. The install command allows setting directory permissions during creation.

Next, we use a playbook to see how the install command works:

$ cat install_directory.yml
---
- hosts: client1
  become: true
  tasks:
    - name:  Using the install command
      command: install -d -m 0755 /opt/install_directory

In the above playbook, install creates a directory if it doesn’t already exist. Also, we’ve set the permission using the mode option.

Further, we run the playbook:

$ ansible-playbook -i project_inventory.ini install_directory.yml 
PLAY [client1] *******************************************************************************
TASK [Gathering Facts] ***********************************************************************
ok: [192.168.29.21]
TASK [Using the install command] ******************************************
changed: [192.168.29.21]
PLAY RECAP **********
192.168.29.21              : ok=2    changed=1 ...
...

The above playbook uses the command module for running shell commands directly on the host. As a result, we can see the changes on the remote machine:

$ ls -l /opt/
drwxr-xr-x 2 root root 4096 Sep 17 04:04 install_directory

Here, the install command creates the directory with -d. This is short for directory. We then set the permissions with -m 0755.

11. Conclusion

In this article, we discussed many ways to add a directory on the remote host with Ansible.

First, we saw how to add a simple directory on the remote machine. Further, we followed up by adding the access permissions. Moving on, we set up multiple and time-stamped directories on the remote host. Then, we saw how to remove them from the remote system.

Finally, we used the install command.