Skip to main content

Command Palette

Search for a command to run...

Day 84: Copy Data to App Servers using Ansible | 100 Days of DevOps

Updated
โ€ข7 min read
R
Iโ€™m currently working in DevOps and documenting my learning journey along the way. From CI/CD pipelines to cloud and containers, Iโ€™m exploring how modern systems are built and deployed. This blog is where I share what I learn, break down concepts in simple terms, and track my progress. Learning one concept at a time, and trying to apply it practically.

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 copy module to transfer files to remote servers
  • Managing directories using the file module
  • Understanding Ansible's idempotent behavior
  • Validating file deployment on remote systems

๐Ÿ”น Task Requirement

As per the Nautilus DevOps team requirements, I needed to:

  1. Create an inventory file:
/home/thor/ansible/inventory

and add all application servers as managed nodes.

  1. 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
  1. 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

100_days_of_devops

Part 1 of 50

Documenting my 100 Days of DevOps journey with hands-on practice from KodeKloud, along with notes, commands, and mini projects