DEV Community

Janak Shrestha
Janak Shrestha

Posted on

Ansible Archive Module

The Nautilus DevOps team has some data on each app server in Stratos DC that they want to copy to a different location. However, they want to create an archive of the data first, then they want to copy the same to a different location on the respective app server. Additionally, there are some specific requirements for each server. Perform the task using Ansible playbook as per requirements mentioned below:

Create a playbook named playbook.yml under /home/thor/ansible directory on jump host, an inventory file is already placed under /home/thor/ansible/ directory on Jump Server itself.

  1. Create an archive beta.tar.gz (make sure archive format is tar.gz) of /usr/src/itadmin/ directory ( present on each app server ) and copy it to /opt/itadmin/ directory on all app servers. The user and group owner of archive beta.tar.gz should be tony for App Server 1steve for App Server 2 and banner for App Server 3.

Note: Validation will try to run playbook using command ansible-playbook -i inventory playbook.yml so please make sure playbook works this way, without passing any extra arguments.


Introduction

In the fast-paced world of DevOps, automation is the key to maintaining efficiency, consistency, and reliability across infrastructure. The Nautilus DevOps team recently faced a challenge: they needed to archive data from multiple application servers and move it to different locations with specific ownership requirements. Instead of performing this task manually on each server, they turned to Ansible—and the results were outstanding.

This comprehensive guide walks through the entire process of creating an Ansible playbook to archive a directory and copy it to a different location with specific ownership requirements across multiple servers.


Table of Contents

  1. Understanding the Requirements
  2. Prerequisites and Setup
  3. Inventory Configuration
  4. Playbook Design and Implementation
  5. Execution and Verification
  6. Troubleshooting Common Issues
  7. Best Practices and Key Takeaways
  8. Conclusion

Understanding the Requirements

The DevOps team had specific requirements for all application servers in the Stratos DC:

Component Details
Source Directory /usr/src/finance/ (exists on each app server)
Destination Directory /opt/finance/ (create if not exists)
Archive Name official.tar.gz
Archive Format tar.gz (compressed archive)
Server Owner & Group
stapp01 tony
stapp02 steve
stapp03 banner

The Task Must Be Performed Using:

ansible-playbook -i inventory playbook.yml
Enter fullscreen mode Exit fullscreen mode

No additional arguments should be required.


Prerequisites and Setup

Before diving into the implementation, ensure the following prerequisites are met:

1. Ansible Installation

# Verify Ansible is installed
ansible --version

# If not installed, install via yum
sudo yum install -y ansible
Enter fullscreen mode Exit fullscreen mode

2. SSH Connectivity

# Test SSH connectivity to each server
ssh -o StrictHostKeyChecking=no tony@stapp01 "echo 'Connected'"
ssh -o StrictHostKeyChecking=no steve@stapp02 "echo 'Connected'"
ssh -o StrictHostKeyChecking=no banner@stapp03 "echo 'Connected'"
Enter fullscreen mode Exit fullscreen mode

3. Directory Structure

# Create the working directory
mkdir -p /home/thor/ansible
cd /home/thor/ansible
Enter fullscreen mode Exit fullscreen mode

Inventory Configuration

The inventory file serves as the foundation for Ansible connectivity. Here's the complete inventory configuration:

cat > /home/thor/ansible/inventory << 'EOF'
[app_servers]
stapp01 ansible_user=tony ansible_ssh_pass=Ir0nM@n ansible_ssh_common_args='-o StrictHostKeyChecking=no'
stapp02 ansible_user=steve ansible_ssh_pass=Am3ric@ ansible_ssh_common_args='-o StrictHostKeyChecking=no'
stapp03 ansible_user=banner ansible_ssh_pass=BigGr33n ansible_ssh_common_args='-o StrictHostKeyChecking=no'
EOF
Enter fullscreen mode Exit fullscreen mode

Inventory Breakdown:

  • Group: [app_servers] - All application servers
  • Host: Unique identifier for each server
  • ansible_user: SSH username for authentication
  • ansible_ssh_pass: SSH password for authentication
  • ansible_ssh_common_args: Additional SSH options
  • StrictHostKeyChecking=no: Disables host key verification

Verify Inventory

# Test inventory connectivity
ansible -i inventory app_servers -m ping
Enter fullscreen mode Exit fullscreen mode

Playbook Design and Implementation

The playbook is designed with idempotency, error handling, and flexibility in mind.

Complete Playbook

cat > /home/thor/ansible/playbook.yml << 'EOF'
---
- name: Archive and copy finance directory across app servers
  hosts: app_servers
  gather_facts: no
  vars:
    source_dir: /usr/src/finance
    dest_dir: /opt/finance
    archive_path: /opt/finance/official.tar.gz

  tasks:
    # Task 1: Create destination directory
    - name: Create destination directory
      file:
        path: "{{ dest_dir }}"
        state: directory
        mode: '0755'
      become: yes

    # Task 2: Verify source directory exists
    - name: Verify source directory exists
      stat:
        path: "{{ source_dir }}"
      register: source_stat
      become: yes
      failed_when: not source_stat.stat.exists

    # Task 3: Remove existing archive if present
    - name: Remove existing archive if present
      file:
        path: "{{ archive_path }}"
        state: absent
      become: yes
      when: source_stat.stat.exists

    # Task 4: Create tar.gz archive
    - name: Create tar.gz archive of finance directory
      archive:
        path: "{{ source_dir }}/"
        dest: "{{ archive_path }}"
        format: gz
        force_archive: yes
      become: yes
      when: source_stat.stat.exists

    # Task 5: Set ownership of the archive
    - name: Set ownership of official.tar.gz
      file:
        path: "{{ archive_path }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      become: yes

    # Task 6: Verify archive exists
    - name: Verify archive exists
      stat:
        path: "{{ archive_path }}"
      register: archive_stat
      become: yes

    # Task 7: Display completion message
    - name: Display completion message
      debug:
        msg: "Successfully created {{ archive_path }} on {{ inventory_hostname }} with owner {{ ansible_user }}"
      when: archive_stat.stat.exists
EOF
Enter fullscreen mode Exit fullscreen mode

Alternative Playbook Using Shell Module

If the archive module is unavailable or problematic:

cat > /home/thor/ansible/playbook.yml << 'EOF'
---
- name: Archive and copy finance directory across app servers
  hosts: app_servers
  gather_facts: no
  vars:
    source_dir: /usr/src/finance
    dest_dir: /opt/finance
    archive_path: /opt/finance/official.tar.gz

  tasks:
    - name: Create destination directory
      file:
        path: "{{ dest_dir }}"
        state: directory
        mode: '0755'
      become: yes

    - name: Verify source directory exists
      stat:
        path: "{{ source_dir }}"
      register: source_stat
      become: yes
      failed_when: not source_stat.stat.exists

    - name: Remove existing archive if present
      file:
        path: "{{ archive_path }}"
        state: absent
      become: yes
      when: source_stat.stat.exists

    - name: Create tar.gz archive using shell
      shell: |
        tar -czf "{{ archive_path }}" -C "{{ source_dir | dirname }}" "{{ source_dir | basename }}"
      become: yes
      when: source_stat.stat.exists
      args:
        creates: "{{ archive_path }}"

    - name: Set ownership of official.tar.gz
      file:
        path: "{{ archive_path }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      become: yes

    - name: Verify archive exists
      stat:
        path: "{{ archive_path }}"
      register: archive_stat
      become: yes

    - name: Display completion message
      debug:
        msg: "Successfully created {{ archive_path }} on {{ inventory_hostname }} with owner {{ ansible_user }}"
      when: archive_stat.stat.exists
EOF
Enter fullscreen mode Exit fullscreen mode

Playbook Features Explained

1. Variables
vars:
  source_dir: /usr/src/finance
  dest_dir: /opt/finance
  archive_path: /opt/finance/official.tar.gz
Enter fullscreen mode Exit fullscreen mode

Variables make the playbook reusable and easier to maintain. Changing the source or destination paths requires only one modification.

2. Directory Creation
- name: Create destination directory
  file:
    path: "{{ dest_dir }}"
    state: directory
    mode: '0755'
  become: yes
Enter fullscreen mode Exit fullscreen mode

Creates the destination directory with proper permissions. The become: yes ensures administrative privileges.

3. Source Verification
- name: Verify source directory exists
  stat:
    path: "{{ source_dir }}"
  register: source_stat
  become: yes
  failed_when: not source_stat.stat.exists
Enter fullscreen mode Exit fullscreen mode

Ensures the source directory exists before attempting to archive. Prevents the playbook from failing unexpectedly.

4. Archive Creation
- name: Create tar.gz archive
  archive:
    path: "{{ source_dir }}/"
    dest: "{{ archive_path }}"
    format: gz
    force_archive: yes
  become: yes
Enter fullscreen mode Exit fullscreen mode

Creates the tar.gz archive using the archive module with format: gz. The force_archive: yes ensures the archive is recreated even if it already exists.

5. Dynamic Ownership
- name: Set ownership of official.tar.gz
  file:
    path: "{{ archive_path }}"
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: '0644'
  become: yes
Enter fullscreen mode Exit fullscreen mode

Uses {{ ansible_user }} to dynamically set the correct owner:

  • stapp01 → tony
  • stapp02 → steve
  • stapp03 → banner
6. Verification
- name: Verify archive exists
  stat:
    path: "{{ archive_path }}"
  register: archive_stat
  become: yes
Enter fullscreen mode Exit fullscreen mode

Confirms the archive was successfully created before displaying the completion message.


Execution and Verification

Running the Playbook

cd /home/thor/ansible
ansible-playbook -i inventory playbook.yml
Enter fullscreen mode Exit fullscreen mode

Expected Output

PLAY [Archive and copy finance directory across app servers] ********************

TASK [Create destination directory] *********************************************
changed: [stapp01]
changed: [stapp02]
changed: [stapp03]

TASK [Verify source directory exists] ******************************************
ok: [stapp01]
ok: [stapp02]
ok: [stapp03]

TASK [Remove existing archive if present] **************************************
ok: [stapp01]
ok: [stapp02]
ok: [stapp03]

TASK [Create tar.gz archive of finance directory] ******************************
changed: [stapp01]
changed: [stapp02]
changed: [stapp03]

TASK [Set ownership of official.tar.gz] ****************************************
changed: [stapp01]
changed: [stapp02]
changed: [stapp03]

TASK [Verify archive exists] ***************************************************
ok: [stapp01]
ok: [stapp02]
ok: [stapp03]

TASK [Display completion message] **********************************************
ok: [stapp01] => {
    "msg": "Successfully created /opt/finance/official.tar.gz on stapp01 with owner tony"
}
ok: [stapp02] => {
    "msg": "Successfully created /opt/finance/official.tar.gz on stapp02 with owner steve"
}
ok: [stapp03] => {
    "msg": "Successfully created /opt/finance/official.tar.gz on stapp03 with owner banner"
}

PLAY RECAP *********************************************************************
stapp01 : ok=7 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
stapp02 : ok=7 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
stapp03 : ok=7 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Enter fullscreen mode Exit fullscreen mode

Verification Commands

# 1. Check if archive exists
ansible -i inventory app_servers -m shell -a "ls -la /opt/finance/official.tar.gz"

# Expected Output:
# stapp01: -rw-r--r-- 1 tony tony 215 /opt/finance/official.tar.gz
# stapp02: -rw-r--r-- 1 steve steve 226 /opt/finance/official.tar.gz
# stapp03: -rw-r--r-- 1 banner banner 210 /opt/finance/official.tar.gz

# 2. Check archive content (should work without errors)
ansible -i inventory app_servers -m shell -a "tar -tzf /opt/finance/official.tar.gz"

# Expected Output:
# stapp01: blog.txt
# stapp02: story.txt
# stapp03: media.txt

# 3. Check ownership
ansible -i inventory app_servers -m shell -a "stat -c '%U:%G %n' /opt/finance/official.tar.gz"

# Expected Output:
# stapp01: tony:tony /opt/finance/official.tar.gz
# stapp02: steve:steve /opt/finance/official.tar.gz
# stapp03: banner:banner /opt/finance/official.tar.gz

# 4. Check archive size
ansible -i inventory app_servers -m shell -a "du -sh /opt/finance/official.tar.gz"

# Expected Output:
# stapp01: 4.0K /opt/finance/official.tar.gz
# stapp02: 4.0K /opt/finance/official.tar.gz
# stapp03: 4.0K /opt/finance/official.tar.gz

# 5. Verify ownership on each server individually
ansible -i inventory stapp01 -m shell -a "stat -c '%U:%G' /opt/finance/official.tar.gz"  # Should show tony:tony
ansible -i inventory stapp02 -m shell -a "stat -c '%U:%G' /opt/finance/official.tar.gz"  # Should show steve:steve
ansible -i inventory stapp03 -m shell -a "stat -c '%U:%G' /opt/finance/official.tar.gz"  # Should show banner:banner
Enter fullscreen mode Exit fullscreen mode

Video Demonstration

For a complete visual guide, watch this demonstration:

Ansible Archive Automation Demo


Troubleshooting Common Issues

Issue 1: Source Directory Doesn't Exist

Symptom:

fatal: [stapp01]: FAILED! => {
    "msg": "Source directory does not exist"
}
Enter fullscreen mode Exit fullscreen mode

Solution:

# Create the directory and test data
ansible -i inventory app_servers -m file -a "path=/usr/src/finance state=directory mode=0755"
ansible -i inventory app_servers -m copy -a "content='Financial data\n' dest=/usr/src/finance/records.txt"
ansible -i inventory app_servers -m copy -a "content='Blog content\n' dest=/usr/src/finance/blog.txt"
Enter fullscreen mode Exit fullscreen mode

Issue 2: Archive Not in Gzip Format

Symptom:

gzip: stdin: not in gzip format
tar: Child returned status 1
Enter fullscreen mode Exit fullscreen mode

Solution:

  • Use the shell module version with tar -czf instead of the archive module
  • Ensure format: gz is specified when using the archive module
# Correct archive module usage
archive:
  path: "/usr/src/finance/"
  dest: "/opt/finance/official.tar.gz"
  format: gz
  force_archive: yes

# Or use shell module
shell: tar -czf /opt/finance/official.tar.gz -C /usr/src finance
Enter fullscreen mode Exit fullscreen mode

Issue 3: Permission Denied

Symptom:

fatal: [stapp01]: FAILED! => {
    "msg": "Permission denied"
}
Enter fullscreen mode Exit fullscreen mode

Solution:

# Ensure 'become: yes' is used for all administrative tasks
# Check sudo privileges for the user
ansible -i inventory app_servers -m shell -a "sudo -l"

# If password is required for sudo, add to inventory
# ansible_become_pass=Ir0nM@n
Enter fullscreen mode Exit fullscreen mode

Issue 4: Wrong Ownership

Symptom:

-rw-r--r-- 1 root root /opt/finance/official.tar.gz
Enter fullscreen mode Exit fullscreen mode

Solution:

# Verify inventory user mapping
cat /home/thor/ansible/inventory

# Ensure ansible_user variable is correct for each server
# stapp01 -> tony
# stapp02 -> steve
# stapp03 -> banner

# Manually fix ownership if needed
ansible -i inventory stapp01 -m file -a "path=/opt/finance/official.tar.gz owner=tony group=tony mode=0644"
Enter fullscreen mode Exit fullscreen mode

Issue 5: Connection Timeout

Symptom:

fatal: [stapp01]: UNREACHABLE! => {
    "msg": "Failed to connect to the host via ssh"
}
Enter fullscreen mode Exit fullscreen mode

Solution:

# Test network connectivity
ping -c 3 stapp01

# Check SSH port
nc -zv stapp01 22

# Verify SSH credentials
ssh -o StrictHostKeyChecking=no tony@stapp01

# Increase timeout in inventory
ansible_ssh_timeout=60
Enter fullscreen mode Exit fullscreen mode

Issue 6: Host Key Verification Failed

Symptom:

Host key verification failed
Enter fullscreen mode Exit fullscreen mode

Solution:

# Add to inventory
ansible_ssh_common_args='-o StrictHostKeyChecking=no'

# Or remove from known_hosts
ssh-keygen -R stapp01
Enter fullscreen mode Exit fullscreen mode

Issue 7: Archive Module Not Available

Symptom:

ERROR! no action detected in task
Enter fullscreen mode Exit fullscreen mode

Solution:

  • Use the shell module alternative
  • Update Ansible to a newer version
sudo yum update -y ansible
Enter fullscreen mode Exit fullscreen mode

Best Practices and Key Takeaways

1. Dynamic Ownership

Using {{ ansible_user }} from the inventory makes the playbook flexible and reusable across different servers with different users. This eliminates hardcoding and simplifies maintenance.

Benefits:

  • No need to create separate playbooks for each server
  • Easy to add new servers with different users
  • Single source of truth for user mapping

2. Idempotency

The playbook can be run multiple times without causing errors or creating duplicate archives.

Implementation:

  • creates argument in shell commands
  • force_archive option in archive module
  • state: absent before creating new archive

3. Error Handling

Using failed_when and conditional execution (when) prevents the playbook from failing unnecessarily.

Best Practices:

  • Always check source existence before operations
  • Use register to capture results
  • Provide meaningful error messages
  • Use ignore_errors for non-critical tasks

4. Verification

Built-in verification tasks provide confidence that the operation was successful on all servers.

Verification Steps:

  • Check if archive exists
  • Verify correct ownership
  • Confirm archive content
  • Validate file permissions

5. Use of Become

Using become: yes for administrative tasks ensures the playbook has the necessary permissions.

When to Use:

  • Creating directories in /opt/
  • Writing to system directories
  • Changing file ownership
  • Installing packages

6. Modular Design

Using variables and reusable tasks makes the playbook modular and maintainable.

Benefits:

  • Easy to update paths
  • Simple to extend functionality
  • Clear separation of concerns

7. Documentation

Inline comments explain the purpose of each task, making the playbook self-documenting.

Documentation Tips:

  • Add task descriptions
  • Explain complex logic
  • Include variable definitions
  • Provide usage examples

Conclusion

The Ansible playbook successfully automates the process of:

  • Directory Management: Creating destination directories with proper permissions
  • Archive Creation: Compressing source directories into tar.gz format
  • File Transfer: Copying archives to target locations
  • Ownership Management: Setting correct ownership based on server requirements
  • Verification: Confirming successful execution on all servers
  • Idempotency: Safe to run multiple times

Results Summary

Server Source File Archive Created Owner Status
stapp01 blog.txt official.tar.gz tony:tony
stapp02 story.txt official.tar.gz steve:steve
stapp03 media.txt official.tar.gz banner:banner

Key Achievements

  1. Time Savings: Automated what would be a manual 15-minute task per server
  2. Consistency: Same process applied across all servers
  3. Reliability: Idempotent execution ensures no duplicate archives
  4. Maintainability: Easy to update for future requirements
  5. Scalability: Can be extended to additional servers

  6. Extend the Playbook: Add additional directories or file types

  7. Implement Scheduling: Use Ansible Tower or cron for regular archiving

  8. Add Monitoring: Implement logging and notification for archive operations

  9. Define Retention Policies: Set up automated cleanup of old archives

  10. Implement Backup Integration: Connect with backup systems for disaster recovery

  11. Create Documentation: Document the playbook for team members

Top comments (0)