Day 84: Copy Data to App Servers using Ansible | 100 Days of DevOps
Content
Today I worked on an Ansible automation task that involved copying a file from the jump host to multiple application servers in the Stratos Datacenter. This exercise helped me understand how Ansible manages file transfers, works with inventories containing multiple hosts, and ensures idempotent operations across servers.
๐น What I Learned
- Creating and managing Ansible inventory files
- Defining multiple managed nodes in an inventory group
- Using the
copymodule to transfer files to remote servers - Managing directories using the
filemodule - Understanding Ansible's idempotent behavior
- Validating file deployment on remote systems
๐น Task Requirement
As per the Nautilus DevOps team requirements, I needed to:
- Create an inventory file:
/home/thor/ansible/inventory
and add all application servers as managed nodes.
- Create a playbook:
/home/thor/ansible/playbook.yml
to copy the following file from the jump host:
/usr/src/finance/index.html
to all application servers under:
/opt/finance
- Ensure the solution works using:
ansible-playbook -i inventory playbook.yml
without requiring any additional arguments.
๐น Steps I Followed
1. Navigated to the Ansible Working Directory
cd /home/thor/ansible
Checked the directory contents:
ls
2. Created the Inventory File
Opened the inventory file:
vi inventory
Added the following content:
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony ansible_password=Ir0nM@n
stapp02 ansible_host=stapp02 ansible_user=steve ansible_password=Am3ric@
stapp03 ansible_host=stapp03 ansible_user=banner ansible_password=BigGr33n
Saved the file.
๐น Understanding the Inventory
Inventory Group
[app_servers]
Creates a host group named app_servers.
This allows the playbook to target all application servers at once.
Managed Hosts
stapp01
stapp02
stapp03
These are the application servers that Ansible will manage.
Authentication Variables
Example:
ansible_user=tony
ansible_password=Ir0nM@n
These values tell Ansible:
- Which user account to use
- Which password to authenticate with
during SSH connections.
Complete Inventory Entry
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony ansible_password=Ir0nM@n
stapp02 ansible_host=stapp02 ansible_user=steve ansible_password=Am3ric@
stapp03 ansible_host=stapp03 ansible_user=banner ansible_password=BigGr33n
๐ In simple terms:
This inventory tells Ansible:
- Which servers to manage
- Which users to connect with
- Which passwords to use
- Which hosts belong to the application server group
3. Verified Connectivity
Before creating the playbook, I tested connectivity:
ansible app_servers -i inventory -m ping
Output:
stapp01 | SUCCESS => pong
stapp02 | SUCCESS => pong
stapp03 | SUCCESS => pong
๐น Why This Step Is Important
This confirms:
- Inventory is configured correctly
- SSH authentication works
- Ansible can reach all target servers
- Python is available on remote hosts
Performing this validation helps prevent troubleshooting later.
4. Verified the Initial State
Before running any automation, I connected to one application server.
ssh tony@stapp01
Checked the target directory:
ls -l /opt/finance
Output:
total 0
Observation:
The directory existed but no files were present.
This served as the baseline state before deployment.
5. Created the Playbook
Opened the playbook file:
vi playbook.yml
Added the following content:
---
- name: Copy finance index file from control/jump host to app servers
hosts: app_servers
become: true
tasks:
- name: Ensure destination directory exists
ansible.builtin.file:
path: /opt/finance
state: directory
mode: '0755'
- name: Copy finance index file
ansible.builtin.copy:
src: /usr/src/finance/index.html
dest: /opt/finance/index.html
mode: '0644'
backup: true
Saved the file.
๐น Understanding the Playbook
Target Hosts
hosts: app_servers
The playbook runs on every host inside the app_servers inventory group.
In this case:
- stapp01
- stapp02
- stapp03
Privilege Escalation
become: true
Allows Ansible to execute tasks using elevated privileges.
This is required because the destination path is under:
/opt
which typically requires root permissions.
File Module
ansible.builtin.file
Used to manage files and directories on remote systems.
Ensuring the Directory Exists
state: directory
Functions similarly to:
mkdir -p /opt/finance
If the directory already exists, Ansible leaves it unchanged.
Directory Permissions
mode: '0755'
Sets permissions as:
Owner : Read Write Execute
Group : Read Execute
Others : Read Execute
Equivalent Linux command:
chmod 755 /opt/finance
Copy Module
ansible.builtin.copy
Transfers files from the Ansible control node (jump host) to remote systems.
Source File
src: /usr/src/finance/index.html
The file located on the jump host.
Destination File
dest: /opt/finance/index.html
The location where the file will be copied on each application server.
File Permissions
mode: '0644'
Sets:
Owner : Read Write
Group : Read
Others : Read
Equivalent Linux command:
chmod 644 /opt/finance/index.html
Backup Option
backup: true
If the destination file already exists, Ansible creates a backup before replacing it.
This provides a simple rollback mechanism.
6. Executed the Playbook
Ran:
ansible-playbook -i inventory playbook.yml
Output:
PLAY [Copy finance index file from control/jump host to app servers]
TASK [Gathering Facts]
ok: [stapp01]
ok: [stapp02]
ok: [stapp03]
TASK [Ensure destination directory exists]
ok: [stapp01]
ok: [stapp02]
ok: [stapp03]
TASK [Copy finance index file]
changed: [stapp01]
changed: [stapp02]
changed: [stapp03]
PLAY RECAP
stapp01 : ok=3 changed=1 failed=0
stapp02 : ok=3 changed=1 failed=0
stapp03 : ok=3 changed=1 failed=0
๐น Understanding the Output
Gathering Facts
TASK [Gathering Facts]
Ansible collects system information before running tasks.
Examples include:
- Hostname
- OS details
- Network information
- Memory information
- Python interpreter location
Directory Task
ok
The directory already existed, so no changes were needed.
Copy Task
changed
The file was copied successfully.
Since the file did not previously exist, Ansible modified the system state.
Play Recap
ok=3
changed=1
failed=0
Meaning:
- Three tasks completed successfully
- One task modified the server
- No failures occurred
7. Validated the Deployment
Connected back to App Server 1:
ssh tony@stapp01
Checked the directory:
ls -l /opt/finance
Output:
-rw-r--r-- 1 root root 35 Jun 23 16:15 index.html
Verification:
- File exists
- Correct permissions applied
- Deployment completed successfully
๐น Why the File Owner Is root
The file appeared as:
root root
because the playbook was executed with:
become: true
and no explicit owner or group was specified.
As a result, the file was created by the root user.
๐น Inventory vs Playbook
One key concept reinforced during this task was the separation between infrastructure definition and automation logic.
Inventory
Defines:
- Which servers to manage
- Authentication details
- Host groups
- Connection settings
Example:
[app_servers]
stapp01
stapp02
stapp03
Playbook
Defines:
- What actions to perform
- Which modules to use
- Which host groups receive the tasks
Example:
hosts: app_servers
and
ansible.builtin.copy
๐น My Understanding
This task strengthened my understanding of how Ansible manages file deployment across multiple servers. I learned that the inventory defines the infrastructure and connection details, while the playbook contains the automation logic.
๐น What I Found Interesting
I found it interesting that Ansible automatically checks whether a file already exists and compares checksums before copying it. This prevents unnecessary file transfers and ensures that only actual changes are applied.
๐ Full notes: GitHub link