Creating directories in Ansible is useful for setting up a structured server environment, such as hosting a web app.
For example, an app might need folders for logs, configuration files, and data. Ansible ensures these directories are created consistently across servers and only if they don’t already exist (idempotency), preventing duplicates or manual checks.
Ansible also lets you set permissions and ownership during directory creation, ensuring the right users or services have access and enhancing security.
This article focuses on creating directories in Ansible using various methods, including the file and—optionally—the command modules. We’ll also discuss some techniques for managing these directories, such as setting up permissions and removing directories.
Here are some of the things you need so you can follow the examples in this article:
- An Ansible installation on your local machine or control node
- A basic understanding of Ansible’s playbook structure
- Access to a target server where Ansible can connect to execute these commands —this could be a local VM or a remote server
Demo description: organizing application logs
For the examples below, let’s assume you’re setting up a server to host a web application that generates logs for monitoring and debugging.
To keep these logs organized, you want to create a directory structure on the server where logs for each day can be stored separately.
This setup will help you and other team members quickly find logs from a specific day or track patterns over time.
In this section, we’ll review several methods for creating directories in Ansible. Each method has strengths and appropriate conditions for use.
1. Using the file module for directory creation
The file module is Ansible’s main tool for creating, modifying, or deleting files and directories. It’s designed to be idempotent, meaning it only takes action if necessary.
Using the file module for directory creation is efficient and straightforward. It ensures the directory exists and applies the permissions in one go. For instance, if you want to create a directory for storing daily logs, your Ansible playbook will look like this:
---
- hosts: all
become: true
tasks:
- name: Create directory for daily logs
ansible.builtin.file:
path: /var/logs/app/daily_logs
state: directory
mode: '0755'
Here’s a breakdown of this playbook:
path
specifies the location where you want the directory to be created.state: directory
tells Ansible to ensure this path is a directory. If it doesn’t exist, Ansible will create it; if it does, Ansible will move on without errors.mode: '0755'
sets the directory’s permissions. The “0755
” code makes the directory readable and executable by others but writable only by the owner. To learn more about the “0755
” code and file permissions, check out this article.
Once you run this playbook, you should see the following output:
2. Using the command module for directory creation
Although the file module is ideal for creating directories in Ansible, you can also use the command module if you need more control or flexibility. This module allows you to run shell commands directly, such as using mkdir
.
If you create another directory for storing daily logs using the command module, your playbook will look like this:
---
- hosts: all
become: true
tasks:
- name: Create directory for daily logs with command module
ansible.builtin.command:
cmd: "mkdir -p /var/logs/app/daily_logs"
Below is a breakdown of this playbook:
cmd: "mkdir -p /var/logs/app/daily_logs"
runs themkdir -p command
, which creates the directory if it doesn’t exist.- The
-p
option ensures that Ansible doesn’t throw an error if parts of the directory structure don’t already exist.
The command module isn’t idempotent on its own, but mkdir -p
makes it safe by preventing errors if the directory already exists.
NOTE: This module is more convenient if you need greater control over the shell environment or want to combine commands. However, it’s not typically idempotent, so it’s best used when a file doesn’t meet your needs.
Once you run this playbook, you should see the following output:
3. Conditional directory creation
Sometimes you might want to create a directory only under specific conditions. For instance, you might want to create the log directory only if another directory, /var/logs/app
, exists already.
This is how to structure your playbook:
---
- hosts: all
become: true
tasks:
- name: Check if the app directory exists
ansible.builtin.stat:
path: /var/logs/app
register: app_dir
- name: Conditionally create directory for daily logs
ansible.builtin.file:
path: /var/logs/app/daily_logs
state: directory
when: app_dir.stat.exists
From the playbook above:
ansible.builtin.stat
on/var/logs/app
: This first task checks if the /var/logs/app directory exists and stores the result in the app_dir variable.when: app_dir.stat.exists
: The when condition in the second task checks if/var/logs/app
exists. If it does, Ansible creates thedaily_logs
subdirectory. If it doesn’t, the task is skipped.
Once you run this playbook, you should see the following output:
4. Creating a directory structure with multiple levels (recursive creation)
Sometimes you need to create a directory structure with several nested levels. The file module can handle this with a simple configuration.
Using the daily logs example, let’s say you want a structure like /var/logs/app/daily_logs/yyyy/mm/dd
where logs are grouped by year, month, and day.
This is how to structure your playbook:
---
- hosts: all
become: true
tasks:
- name: Create nested directory structure for daily logs
ansible.builtin.file:
path: /var/logs/app/daily_logs/2024/11/07
state: directory
mode: '0755'
By specifying a deeply nested path in path, Ansible will automatically create any missing parent directories, making recursive creation straightforward.
Once you run this playbook, you should see the following output:
5. Creating multiple directories file (iterative creation)
Let’s say you need to create multiple directories in a single task. You can do this by iterating over a list of directories.
This technique is efficient for setting up multiple directories in a single task. It’s particularly useful if your app needs multiple folders organized separately for different types of data or configuration files.
For instance, let’s say you want to create folders for logs, config, and data for the app. You’ll write your playbook like this:
---
- hosts: all
become: true
tasks:
- name: Create multiple directories for app
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: '0755'
loop:
- /var/logs/app
- /var/config/app
- /var/data/app
In this playbook, loop
allows you to iterate over each path in the list, creating each one in turn.
Once you run this playbook, you should see the following output:
Now that you know how to create directories, you’ll need to know how to manage them. The following sections will take you through some common cases you might encounter while working with directories using Ansible:
Specifying permissions
When you create directories in Ansible, setting permissions is essential to control who can read, write, or execute files within those directories. Permissions are managed using the mode parameter, which sets Unix-style permissions.
Using the scenario with the application logs directory, let’s say you want to set permissions so that only the owner has full access while others can only read and execute.
You’ll write your playbook like this:
---
- hosts: all
become: true
tasks:
- name: Create a directory with specific permissions
ansible.builtin.file:
path: /var/logs/app/secure_logs
state: directory
mode: '0700'
owner: root
group: root
From the playbook above:
mode: '0700'
sets permissions so that only the directory’s owner can read, write, and execute files in it. Others won’t have access.owner
andgroup
define who owns the directory and which group has permission to access it. Here, we’re setting both to root for higher security.
Executing the playbook above will give you the following output:
Handling existing directories
As stated above, the file module is naturally idempotent, meaning it won’t try to recreate a directory that already exists. However, you may want to update properties like permissions or ownership on an existing directory without disrupting its contents.
For example, let’s say your /var/logs/app/daily_logs
directory already exists, but you want to update its permissions to be more restrictive.
You’ll create your playbook like this:
---
- hosts: all
become: true
tasks:
- name: Update permissions on an existing directory
ansible.builtin.file:
path: /var/logs/app/daily_logs
state: directory
mode: '0750'
owner: appuser
group: appgroup
With this playbook, even if the directory exists already, setting state: directory allows Ansible to update the permissions, owner, and group. Ansible will skip creating the directory again but will apply any changes in the task.
Once you execute the playbook above, you’ll get the following output:
Removing a directory
Sometimes, you need to clean up or reorganize by deleting directories that are no longer needed. The file module allows you to remove a directory by setting state: absent
.
If you decide to remove an old directory, /var/logs/app/old_logs
, to free up space or tidy up your server, you’ll write your playbook like this:
---
- hosts: all
become: true
tasks:
- name: Remove old log directory
ansible.builtin.file:
path: /var/logs/app/old_logs
state: absent
The state: absent
field tells Ansible to delete the specified directory along with any contents it may contain.
This is what your output will look like:
Handling symbolic links
In certain cases, you might need to create symbolic links (shortcuts) to directories or files located elsewhere on the system.
This is especially handy if you want quick access to shared resources from multiple locations without duplicating files. The file module can create these symbolic links using state: link.
For instance, let’s say you want to create a symbolic link in /var/logs/app/
that points to another directory, /shared/logs/app/
, so users can easily access it.
You can do this using the following playbook:
---
- hosts: all
become: true
tasks:
- name: Ensure the source directory exists
ansible.builtin.file:
path: /shared/logs/app
state: directory
- name: Create a symbolic link to shared logs
ansible.builtin.file:
src: /shared/logs/app
dest: /var/logs/app/shared_logs
state: link
From the playbook above, you have two tasks. The first ensures the source directory exists by creating it using the file module. For the second task:
src
is the original directory to which the link will point.dest
is where the symbolic link will be created.state: link
tells Ansible to create a symbolic link at the specified destination.
Once you run the playbook above, you should get the following output:
Creating directories in Ansible is straightforward, but following these best practices can make configurations across your servers cleaner, more organized, and more secure.
Below are a few things to keep in mind when creating directories in Ansible:
- Use idempotency to your advantage: Ansible’s idempotency ensures that tasks only run when necessary. By defining a directory creation task with state: directory, Ansible will check if the directory already exists and only create it if it doesn’t.
This makes your playbooks resilient, allowing them to be re-run without duplicating or overwriting existing directories, which can be useful when managing multiple servers.
- Set permissions carefully: When creating directories, set permissions that specify exactly who can access or modify them. Using permissions (such as read, write, and execute) along with ownership rules ensures that sensitive directories — like those containing logs or configuration files — remain accessible only to authorized users. This is a crucial security measure to protect your data.
- Avoid hardcoding paths: Yes, we hardcoded paths in this article, but that’s for demo purposes. Use variables instead of embedding specific paths directly in your playbooks. This makes your playbooks adaptable to different environments, like production or staging. You can simply update the variables rather than rewriting paths, saving time and making your code easier to maintain.
- Organize directory structures: For setups with multiple directories, structure them logically and consistently. This could mean setting up main directories with nested folders (like year, month, or project-based structures). Organizing directories makes it easier for your team to navigate, understand, and maintain, and it keeps file systems clean.
- Test with different environments: Before deploying playbooks in production, test them in a safe environment to catch any issues early. This is especially important when working with directory structures, permissions, or variables that might differ across environments. Testing helps ensure your playbooks run smoothly and predictably, minimizing the risk of unexpected outcomes.
Spacelift’s vibrant ecosystem and excellent GitOps flow can greatly assist you in managing and orchestrating Ansible. By introducing Spacelift on top of Ansible, you can easily create custom workflows based on pull requests and apply any necessary compliance checks for your organization.
Another advantage of using Spacelift is that you can manage different infrastructure tools like Ansible, Terraform, Pulumi, AWS CloudFormation, and even Kubernetes from the same place and combine their stacks with building workflows across tools.
Our latest Ansible enhancements solve three of the biggest challenges engineers face when they are using Ansible:
- Having a centralized place in which you can run your playbooks
- Combining IaC with configuration management to create a single workflow
- Getting insights into what ran and where
Provisioning, configuring, governing, and even orchestrating your containers can be performed with a single workflow, separating the elements into smaller chunks to identify issues more easily.
Would you like to see this in action — or just get a tl;dr? Check out this video showing you Spacelift’s new Ansible functionality:
If you want to learn more about using Spacelift with Ansible, check our documentation, read our Ansible guide, or book a demo with one of our engineers.
In this article, we covered the essential techniques for creating and managing directories in Ansible. We started with the basics of creating directories using the file and command modules and explored how Ansible’s Idempotency ensures that directories are created only when needed. This means your playbooks can run without duplications or unintended changes.
We also discussed some best practices when dealing with directories in Ansible. These include setting permissions directly within playbooks to improve security and using variables for paths instead of hardcoding them for flexibility.
Creating directories in Ansible is a foundational task, and mastering these techniques allows you to manage your infrastructure effectively and confidently.
Manage Ansible Better with Spacelift
Managing large-scale playbook execution is hard. Spacelift enables you to automate Ansible playbook execution with visibility and control over resources, and seamlessly link provisioning and configuration workflows.