DEV Community

Janak Shrestha
Janak Shrestha

Posted on

Ansible Unarchive Module

One of the DevOps team members has created a zip archive on jump host in Stratos DC that needs to be extracted and copied over to all app servers in Stratos DC itself. Because this is a routine task, the Nautilus DevOps team has suggested automating it. We can use Ansible since we have been using it for other automation tasks. Below you can find more details about the task:

We have an inventory file under /home/thor/ansible directory on jump host, which should have all the app servers added already.

There is a zip archive /usr/src/data/datacenter.zip on jump host.

Create a playbook.yml under /home/thor/ansible/ directory on jump host itself to perform the below given tasks.

  1. Unzip /usr/src/data/datacenter.zip archive in /opt/data/ location on all app servers.

  2. Make sure the extracted data must has the respective sudo user as their user and group owner, i.e tony for app server 1, steve for app server 2, banner for app server 3.

  3. The extracted data permissions must be 0655.

Note: Validation will try to run the 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 world of DevOps, managing files across multiple servers is a routine but critical task. Whether you're deploying applications, backing up data, or distributing configuration files, the ability to efficiently compress, transfer, and extract files is essential. Ansible, the powerful automation tool, provides two specialized modules—archive and unarchive—that make these operations seamless and reliable.

This comprehensive guide covers everything you need to know about Ansible's archive and unarchive modules, including:

  • Module Deep Dives: Understanding archive and unarchive in detail
  • Inventory Best Practices: Creating robust inventory files
  • Playbook Design: Building production-ready playbooks
  • Real-World Examples: Complete working examples from actual tasks
  • Troubleshooting: Common issues and their solutions
  • Security Best Practices: Protecting sensitive data
  • Performance Optimization: Tips for faster execution

📚 Table of Contents

  1. Understanding the Modules
  2. Inventory File Best Practices
  3. The Archive Module: Creating Archives
  4. The Unarchive Module: Extracting Archives
  5. Real-World Implementation
  6. Verification and Testing
  7. Troubleshooting Guide
  8. Security Best Practices
  9. Performance Optimization
  10. Conclusion

1. Understanding the Modules

📦 The Archive Module

The archive module creates compressed archives of files and directories on target hosts.

Key Features:

  • Supports multiple formats: tar, gz, bz2, zip
  • Preserves file permissions and ownership
  • Can remove original files after archiving
  • Idempotent - skips if archive already exists with correct content

Basic Syntax:

- name: Create a tar.gz archive
  archive:
    path: /path/to/source/directory/
    dest: /path/to/archive.tar.gz
    format: gz
    force_archive: yes
    owner: username
    group: groupname
    mode: '0644'
Enter fullscreen mode Exit fullscreen mode

📂 The Unarchive Module

The unarchive module extracts archives on target hosts.

Key Features:

  • Supports multiple formats: tar, gz, bz2, zip
  • Can extract from local or remote sources
  • Handles absolute/relative paths intelligently
  • Idempotent - skips if already extracted

Basic Syntax:

- name: Extract a zip archive
  unarchive:
    src: /path/to/source/archive.zip
    dest: /path/to/destination/
    remote_src: no
    owner: username
    group: groupname
    mode: '0644'
Enter fullscreen mode Exit fullscreen mode

2. Inventory File Best Practices

📋 Creating the Perfect Inventory

A well-structured inventory file is the foundation of successful Ansible automation.

Basic Inventory Structure

[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony ansible_ssh_pass=Ir0nM@n
stapp02 ansible_host=stapp02 ansible_user=steve ansible_ssh_pass=Am3ric@
stapp03 ansible_host=stapp03 ansible_user=banner ansible_ssh_pass=BigGr33n
Enter fullscreen mode Exit fullscreen mode

Production-Ready Inventory

# Group: Application Servers
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony
stapp02 ansible_host=stapp02 ansible_user=steve
stapp03 ansible_host=stapp03 ansible_user=banner

# Group-specific variables
[app_servers:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
ansible_ssh_private_key_file=/home/thor/.ssh/id_rsa
ansible_python_interpreter=/usr/bin/python3

# Windows Server Group Example
[windows_servers]
db1 ansible_host=server4.company.com ansible_user=administrator

[windows_servers:vars]
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
ansible_password=Dbp@ss123!
Enter fullscreen mode Exit fullscreen mode

Advanced Inventory with YAML Format

all:
  children:
    app_servers:
      hosts:
        stapp01:
          ansible_host: stapp01
          ansible_user: tony
        stapp02:
          ansible_host: stapp02
          ansible_user: steve
        stapp03:
          ansible_host: stapp03
          ansible_user: banner
      vars:
        ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
        ansible_ssh_private_key_file: /home/thor/.ssh/id_rsa
Enter fullscreen mode Exit fullscreen mode

🔑 Security Best Practices for Inventory

1. Use SSH Keys Instead of Passwords

# Generate SSH key
ssh-keygen -t rsa -b 4096 -C "thor@jump-host"

# Copy to servers
ssh-copy-id tony@stapp01
ssh-copy-id steve@stapp02
ssh-copy-id banner@stapp03

# Inventory without passwords
stapp01 ansible_host=stapp01 ansible_user=tony
stapp02 ansible_host=stapp02 ansible_user=steve
stapp03 ansible_host=stapp03 ansible_user=banner
Enter fullscreen mode Exit fullscreen mode

2. Use Ansible Vault for Secrets

# Create a vault file for passwords
ansible-vault create group_vars/all/vault.yml

# Content of vault.yml
---
vault_ssh_pass: "Ir0nM@n"
vault_sudo_pass: "Ir0nM@n"

# Inventory referencing vault
stapp01 ansible_host=stapp01 ansible_user=tony ansible_ssh_pass={{ vault_ssh_pass }}
Enter fullscreen mode Exit fullscreen mode

3. Use Separate Variable Files

ansible/
├── inventory/
│   └── production
├── group_vars/
│   └── app_servers.yml
└── host_vars/
    ├── stapp01.yml
    ├── stapp02.yml
    └── stapp03.yml
Enter fullscreen mode Exit fullscreen mode

3. The Archive Module: Creating Archives

📦 Complete Guide to Archive Module

Example 1: Create Simple Tar Archive

---
- name: Create archive examples
  hosts: app_servers
  gather_facts: no
  tasks:
    - name: Create tar.gz of /usr/src/data
      archive:
        path: /usr/src/data/
        dest: /opt/backup/data_backup.tar.gz
        format: gz
        force_archive: yes
      become: yes
Enter fullscreen mode Exit fullscreen mode

Example 2: Create Zip Archive with Specific Permissions

---
- name: Create zip archive with permissions
  hosts: app_servers
  gather_facts: no
  tasks:
    - name: Create zip archive
      archive:
        path: /usr/src/itadmin/
        dest: /opt/itadmin/beta.zip
        format: zip
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
        force_archive: yes
      become: yes
Enter fullscreen mode Exit fullscreen mode

Example 3: Archive with Multiple Paths

---
- name: Archive multiple directories
  hosts: app_servers
  gather_facts: no
  vars:
    archive_files:
      - /var/log/app
      - /etc/nginx
      - /usr/src/data
  tasks:
    - name: Create archive from multiple directories
      archive:
        path: "{{ archive_files }}"
        dest: /opt/backup/full_backup.tar.gz
        format: gz
        remove: no
      become: yes
Enter fullscreen mode Exit fullscreen mode

Example 4: Advanced Archive with Error Handling

---
- name: Production-ready archive creation
  hosts: app_servers
  gather_facts: no
  vars:
    source_dirs:
      - /usr/src/data
      - /etc/config
    dest_dir: /opt/backup
    archive_name: "backup_{{ ansible_date_time.date }}.tar.gz"
    archive_path: "/opt/backup/backup_{{ ansible_date_time.date }}.tar.gz"

  tasks:
    - name: Create backup directory
      file:
        path: "{{ dest_dir }}"
        state: directory
        mode: '0755'
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
      become: yes

    - name: Check source directories exist
      stat:
        path: "{{ item }}"
      register: dir_stats
      with_items: "{{ source_dirs }}"
      become: yes
      failed_when: not item.stat.exists

    - name: Create archive of all source directories
      archive:
        path: "{{ source_dirs }}"
        dest: "{{ archive_path }}"
        format: gz
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
        force_archive: yes
      become: yes
      register: archive_result

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

    - name: Get archive size
      command: "du -sh {{ archive_path }}"
      register: archive_size
      become: yes
      changed_when: false

    - name: Display archive details
      debug:
        msg:
          - " Archive created successfully on {{ inventory_hostname }}"
          - "📁 Source: {{ source_dirs | join(', ') }}"
          - "📦 Destination: {{ archive_path }}"
          - "📊 Size: {{ archive_size.stdout }}"
          - "👤 Owner: {{ ansible_user }}"
Enter fullscreen mode Exit fullscreen mode

4. The Unarchive Module: Extracting Archives

📂 Complete Guide to Unarchive Module

Example 1: Extract from Jump Host to Remote Servers

---
- name: Extract zip archive from jump host
  hosts: app_servers
  gather_facts: no
  vars:
    source_archive: /usr/src/data/datacenter.zip
    dest_dir: /opt/data

  tasks:
    - name: Verify source archive exists
      local_action:
        module: stat
        path: "{{ source_archive }}"
      register: archive_stat
      run_once: yes
      failed_when: not archive_stat.stat.exists

    - name: Extract archive to destination
      unarchive:
        src: "{{ source_archive }}"
        dest: "{{ dest_dir }}"
        remote_src: no
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0655'
      become: yes
Enter fullscreen mode Exit fullscreen mode

Example 2: Extract Remote Archive

---
- name: Extract remote archive
  hosts: app_servers
  gather_facts: no
  vars:
    archive_path: /tmp/datacenter.zip
    dest_dir: /opt/data

  tasks:
    - name: Extract archive (already on remote)
      unarchive:
        src: "{{ archive_path }}"
        dest: "{{ dest_dir }}"
        remote_src: yes
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0655'
      become: yes
Enter fullscreen mode Exit fullscreen mode

Example 3: Extract with Additional Options

---
- name: Advanced unarchive with options
  hosts: app_servers
  gather_facts: no
  vars:
    source_archive: /usr/src/data/config.tar.gz
    dest_dir: /opt/config

  tasks:
    - name: Ensure unzip/tar is installed
      package:
        name:
          - unzip
          - tar
        state: present
      become: yes

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

    - name: Extract archive with specific parameters
      unarchive:
        src: "{{ source_archive }}"
        dest: "{{ dest_dir }}"
        remote_src: no
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
        extra_opts: [--strip-components=1]
        list_files: yes
      become: yes
      register: extract_result

    - name: Display extracted files
      debug:
        msg: "Extracted files: {{ extract_result.files }}"
Enter fullscreen mode Exit fullscreen mode

Example 4: Complete Production Unarchive Playbook

---
- name: Production-ready archive extraction
  hosts: app_servers
  gather_facts: yes
  vars:
    source_archive: /usr/src/data/datacenter.zip
    dest_dir: /opt/data
    temp_dir: /tmp/ansible_extract

  tasks:
    # Pre-flight checks
    - name: Verify source archive exists on controller
      local_action:
        module: stat
        path: "{{ source_archive }}"
      register: archive_stat
      run_once: yes
      failed_when: not archive_stat.stat.exists

    - name: Display archive information
      debug:
        msg:
          - "📦 Archive: {{ source_archive }}"
          - "📊 Size: {{ (archive_stat.stat.size / 1024) | int }} KB"
          - "📅 Modified: {{ archive_stat.stat.mtime }}"
      run_once: yes

    # Dependencies
    - name: Ensure required packages are installed
      package:
        name:
          - unzip
          - tar
          - gzip
        state: present
      become: yes

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

    # Archive copy and extraction
    - name: Copy archive to remote host
      copy:
        src: "{{ source_archive }}"
        dest: "{{ temp_dir }}/{{ source_archive | basename }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      become: yes

    - name: Extract archive
      unarchive:
        src: "{{ temp_dir }}/{{ source_archive | basename }}"
        dest: "{{ dest_dir }}"
        remote_src: yes
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0655'
        list_files: yes
      become: yes
      register: extract_result

    # Cleanup
    - name: Remove temporary archive
      file:
        path: "{{ temp_dir }}"
        state: absent
      become: yes

    # Verification
    - name: Verify extraction
      shell: "find {{ dest_dir }} -type f | wc -l"
      register: file_count
      become: yes
      changed_when: false

    - name: Verify correct ownership
      shell: "find {{ dest_dir }} -type f -user {{ ansible_user }} -group {{ ansible_user }} | wc -l"
      register: owner_count
      become: yes
      changed_when: false

    - name: Verify permissions
      shell: "find {{ dest_dir }} -type f -perm 0655 | wc -l"
      register: perm_count
      become: yes
      changed_when: false

    # Results
    - name: Display extraction summary
      debug:
        msg:
          - " Extraction completed on {{ inventory_hostname }}"
          - "📁 Destination: {{ dest_dir }}"
          - "📄 Total files: {{ file_count.stdout }}"
          - "👤 Correct owner: {{ owner_count.stdout }} files"
          - "🔒 Correct permissions: {{ perm_count.stdout }} files"
          - "👤 Owner: {{ ansible_user }}"
Enter fullscreen mode Exit fullscreen mode

5. Real-World Implementation

🏢 Complete Task Example: Data Center Archive Management

This section combines both archive and unarchive modules in a complete, real-world scenario.

Project Structure

ansible/
├── inventory
├── playbook_archive.yml
├── playbook_unarchive.yml
├── group_vars/
│   └── app_servers.yml
├── host_vars/
│   ├── stapp01.yml
│   ├── stapp02.yml
│   └── stapp03.yml
└── roles/
    └── archive_manager/
        ├── tasks/
        │   ├── main.yml
        │   ├── create_archive.yml
        │   └── extract_archive.yml
        └── vars/
            └── main.yml
Enter fullscreen mode Exit fullscreen mode

Inventory File (Production)

# /home/thor/ansible/inventory

[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony
stapp02 ansible_host=stapp02 ansible_user=steve
stapp03 ansible_host=stapp03 ansible_user=banner

[app_servers:vars]
ansible_ssh_common_args='-o StrictHostKeyChecking=no'
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=/home/thor/.ssh/id_rsa

# Windows Servers (if needed)
[db_servers]
db1 ansible_host=server4.company.com ansible_user=administrator

[db_servers:vars]
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore
ansible_password=Dbp@ss123!
Enter fullscreen mode Exit fullscreen mode

Complete Archive Creation Playbook

# /home/thor/ansible/playbook_archive.yml
---
- name: Create and manage archives across application servers
  hosts: app_servers
  gather_facts: yes
  vars:
    archive_config:
      source_dir: /usr/src/data
      dest_dir: /opt/backup
      archive_name: data_backup.tar.gz
      archive_path: /opt/backup/data_backup.tar.gz
      format: gz
      permissions: '0644'

  tasks:
    # 1. Environment Setup
    - name: Create backup directory
      file:
        path: "{{ archive_config.dest_dir }}"
        state: directory
        mode: '0755'
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
      become: yes

    - name: Check disk space
      shell: "df -h {{ archive_config.dest_dir }} | awk 'NR==2 {print $5}' | sed 's/%//'"
      register: disk_usage
      become: yes
      changed_when: false

    - name: Fail if disk usage is high
      fail:
        msg: "Disk usage is at {{ disk_usage.stdout }}%, please free up space"
      when: disk_usage.stdout | int > 80

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

    - name: Get source directory size
      command: "du -sh {{ archive_config.source_dir }}"
      register: source_size
      become: yes
      changed_when: false

    - name: Count files in source
      command: "find {{ archive_config.source_dir }} -type f | wc -l"
      register: file_count
      become: yes
      changed_when: false

    - name: Display source information
      debug:
        msg:
          - "📁 Source: {{ archive_config.source_dir }}"
          - "📊 Size: {{ source_size.stdout }}"
          - "📄 Files: {{ file_count.stdout }}"

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

    - name: Create tar.gz archive
      archive:
        path: "{{ archive_config.source_dir }}/"
        dest: "{{ archive_config.archive_path }}"
        format: "{{ archive_config.format }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: "{{ archive_config.permissions }}"
        force_archive: yes
      become: yes
      when: source_stat.stat.exists
      register: archive_result

    # 4. Verification
    - name: Verify archive creation
      stat:
        path: "{{ archive_config.archive_path }}"
      register: archive_stat
      become: yes

    - name: Get archive size
      command: "du -sh {{ archive_config.archive_path }}"
      register: archive_size
      become: yes
      changed_when: false

    - name: Verify archive contents
      command: "tar -tzf {{ archive_config.archive_path }} | head -10"
      register: archive_contents
      become: yes
      changed_when: false

    # 5. Results
    - name: Display success message
      debug:
        msg:
          - " Archive created successfully on {{ inventory_hostname }}"
          - "📦 File: {{ archive_config.archive_path }}"
          - "📊 Size: {{ archive_size.stdout }}"
          - "👤 Owner: {{ ansible_user }}"
          - "🔒 Permissions: {{ archive_config.permissions }}"
          - "📄 Contents preview:"
          - "{{ archive_contents.stdout_lines | to_nice_yaml }}"
      when: archive_stat.stat.exists
Enter fullscreen mode Exit fullscreen mode

Complete Unarchive Playbook

# /home/thor/ansible/playbook_unarchive.yml
---
- name: Extract and distribute archives across application servers
  hosts: app_servers
  gather_facts: yes
  vars:
    unarchive_config:
      source_archive: /usr/src/data/datacenter.zip
      dest_dir: /opt/data
      permissions: '0655'

  tasks:
    # 1. Pre-flight Checks
    - name: Verify source archive exists on controller
      local_action:
        module: stat
        path: "{{ unarchive_config.source_archive }}"
      register: archive_stat
      run_once: yes
      failed_when: not archive_stat.stat.exists

    - name: Display archive information
      debug:
        msg:
          - "📦 Archive: {{ unarchive_config.source_archive }}"
          - "📊 Size: {{ (archive_stat.stat.size / 1024) | int }} KB"
          - "📅 Last modified: {{ archive_stat.stat.mtime }}"
      run_once: yes

    # 2. Dependencies
    - name: Ensure required packages are installed
      package:
        name:
          - unzip
          - tar
          - gzip
        state: present
      become: yes
      when: ansible_system == "Linux"

    - name: Create temporary directory
      file:
        path: /tmp/ansible_extract
        state: directory
        mode: '0755'
      become: yes

    # 3. Directory Setup
    - name: Create destination directory
      file:
        path: "{{ unarchive_config.dest_dir }}"
        state: directory
        mode: '0755'
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
      become: yes

    # 4. Archive Copy
    - name: Copy archive to remote host
      copy:
        src: "{{ unarchive_config.source_archive }}"
        dest: "/tmp/ansible_extract/{{ unarchive_config.source_archive | basename }}"
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: '0644'
      become: yes

    # 5. Extraction
    - name: Extract archive
      unarchive:
        src: "/tmp/ansible_extract/{{ unarchive_config.source_archive | basename }}"
        dest: "{{ unarchive_config.dest_dir }}"
        remote_src: yes
        owner: "{{ ansible_user }}"
        group: "{{ ansible_user }}"
        mode: "{{ unarchive_config.permissions }}"
        list_files: yes
      become: yes
      register: extract_result

    # 6. Cleanup
    - name: Remove temporary archive
      file:
        path: /tmp/ansible_extract
        state: absent
      become: yes

    # 7. Verification
    - name: Verify extraction
      shell: "find {{ unarchive_config.dest_dir }} -type f"
      register: extracted_files
      become: yes
      changed_when: false

    - name: Count extracted files
      shell: "find {{ unarchive_config.dest_dir }} -type f | wc -l"
      register: file_count
      become: yes
      changed_when: false

    - name: Check permissions
      shell: "find {{ unarchive_config.dest_dir }} -type f -perm {{ unarchive_config.permissions }} | wc -l"
      register: perm_count
      become: yes
      changed_when: false

    - name: Check ownership
      shell: "find {{ unarchive_config.dest_dir }} -type f -user {{ ansible_user }} -group {{ ansible_user }} | wc -l"
      register: owner_count
      become: yes
      changed_when: false

    # 8. Results
    - name: Display extraction summary
      debug:
        msg:
          - " Extraction completed on {{ inventory_hostname }}"
          - "📁 Destination: {{ unarchive_config.dest_dir }}"
          - "📄 Total files: {{ file_count.stdout }}"
          - "👤 Correct owner: {{ owner_count.stdout }} files"
          - "🔒 Correct permissions: {{ perm_count.stdout }} files"
          - "📋 First 5 files:"
          - "{{ extracted_files.stdout_lines[:5] | to_nice_yaml }}"
      when: file_count.stdout | int > 0
Enter fullscreen mode Exit fullscreen mode

6. Verification and Testing

🔍 Comprehensive Verification Commands

1. Archive Verification Commands

# Check if archive exists
ansible -i inventory app_servers -m stat -a "path=/opt/backup/data_backup.tar.gz"

# List archive contents
ansible -i inventory app_servers -m command -a "tar -tzf /opt/backup/data_backup.tar.gz"

# Check archive size
ansible -i inventory app_servers -m command -a "du -sh /opt/backup/data_backup.tar.gz"

# Verify checksum
ansible -i inventory app_servers -m command -a "md5sum /opt/backup/data_backup.tar.gz"

# Check permissions
ansible -i inventory app_servers -m command -a "stat -c '%a %n' /opt/backup/data_backup.tar.gz"

# Verify ownership
ansible -i inventory app_servers -m command -a "stat -c '%U:%G %n' /opt/backup/data_backup.tar.gz"
Enter fullscreen mode Exit fullscreen mode

2. Unarchive Verification Commands

# List destination directory
ansible -i inventory app_servers -m command -a "ls -la /opt/data/"

# Recursive listing
ansible -i inventory app_servers -m command -a "find /opt/data -type f -ls"

# Count files
ansible -i inventory app_servers -m command -a "find /opt/data -type f | wc -l"

# Check permissions recursively
ansible -i inventory app_servers -m command -a "find /opt/data -type f -exec stat -c '%a %n' {} \;"

# Check ownership recursively
ansible -i inventory app_servers -m command -a "find /opt/data -type f -exec stat -c '%U:%G %n' {} \;"

# Verify file content
ansible -i inventory app_servers -m command -a "head -5 /opt/data/*.txt 2>/dev/null || echo 'No text files'"
Enter fullscreen mode Exit fullscreen mode

3. Complete Verification Script

#!/bin/bash
# verify_ansible_task.sh

echo "========================================="
echo "Ansible Archive Management Verification"
echo "========================================="

INVENTORY="/home/thor/ansible/inventory"
SERVERS="app_servers"

echo -e "\n1️⃣ Checking Archive Existence..."
ansible -i $INVENTORY $SERVERS -m stat -a "path=/opt/backup/data_backup.tar.gz"

echo -e "\n2️⃣ Checking Archive Contents..."
ansible -i $INVENTORY $SERVERS -m shell -a "tar -tzf /opt/backup/data_backup.tar.gz | head -5"

echo -e "\n3️⃣ Checking Archive Permissions..."
ansible -i $INVENTORY $SERVERS -m shell -a "stat -c 'Permissions: %a | Owner: %U:%G | Size: %s | File: %n' /opt/backup/data_backup.tar.gz"

echo -e "\n4️⃣ Verifying Extraction..."
ansible -i $INVENTORY $SERVERS -m shell -a "ls -la /opt/data/"

echo -e "\n5️⃣ Counting Extracted Files..."
ansible -i $INVENTORY $SERVERS -m shell -a "find /opt/data -type f | wc -l"

echo -e "\n6️⃣ Checking Extraction Permissions..."
ansible -i $INVENTORY $SERVERS -m shell -a "find /opt/data -type f -exec stat -c 'Permissions: %a | Owner: %U:%G | File: %n' {} \;"

echo -e "\n✅ Verification Complete!"
Enter fullscreen mode Exit fullscreen mode

4. Automated Test Playbook

---
- name: Validate archive management tasks
  hosts: app_servers
  gather_facts: yes
  tasks:
    - name: Test archive existence
      stat:
        path: /opt/backup/data_backup.tar.gz
      register: archive_check
      failed_when: not archive_check.stat.exists

    - name: Test archive size
      command: "du -sh /opt/backup/data_backup.tar.gz"
      register: archive_size
      changed_when: false

    - name: Test archive permissions
      command: "stat -c '%a' /opt/backup/data_backup.tar.gz"
      register: archive_perm
      changed_when: false
      failed_when: archive_perm.stdout != "644"

    - name: Test extraction directory
      stat:
        path: /opt/data
      register: extract_check
      failed_when: not extract_check.stat.exists

    - name: Count extracted files
      shell: "find /opt/data -type f | wc -l"
      register: file_count
      changed_when: false
      failed_when: file_count.stdout | int == 0

    - name: Validate all tests passed
      debug:
        msg:
          - " All validation tests passed on {{ inventory_hostname }}"
          - "📦 Archive: /opt/backup/data_backup.tar.gz ({{ archive_size.stdout }})"
          - "🔒 Permissions: {{ archive_perm.stdout }}"
          - "📄 Extracted files: {{ file_count.stdout }}"
Enter fullscreen mode Exit fullscreen mode

7. Troubleshooting Guide

🔧 Common Issues and Solutions

Issue 1: Source Archive Not Found

Symptom:

fatal: [stapp01]: FAILED! => {
    "msg": "Source file /usr/src/data/datacenter.zip does not exist"
}
Enter fullscreen mode Exit fullscreen mode

Solution:

# Create the archive
sudo mkdir -p /usr/src/data
sudo touch /usr/src/data/file{1,2,3}.txt
sudo zip -r /usr/src/data/datacenter.zip /usr/src/data/*.txt

# Or create using ansible
ansible localhost -m file -a "path=/usr/src/data state=directory"
ansible localhost -m shell -a "touch /usr/src/data/file{1,2,3}.txt"
ansible localhost -m shell -a "zip -r /usr/src/data/datacenter.zip /usr/src/data/*.txt"
Enter fullscreen mode Exit fullscreen mode

Issue 2: Permission Denied

Symptom:

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

Solution:

# Always use become: yes for system directories
- name: Create directory
  file:
    path: /opt/data
    state: directory
  become: yes

# Check sudo access
ansible -i inventory app_servers -m shell -a "sudo -l"
Enter fullscreen mode Exit fullscreen mode

Issue 3: Archive Not in Correct Format

Symptom:

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

Solution:

# For archive module, use format: gz
- name: Create gzip archive
  archive:
    path: /usr/src/data/
    dest: /opt/backup/data.tar.gz
    format: gz  # Correct for .tar.gz
    force_archive: yes

# For unarchive, module auto-detects format
- name: Extract archive
  unarchive:
    src: /usr/src/data/datacenter.zip
    dest: /opt/data/
    remote_src: no
Enter fullscreen mode Exit fullscreen mode

Issue 4: Unzip Not Installed

Symptom:

fatal: [stapp01]: FAILED! => {
    "msg": "unzip is required for unarchive module"
}
Enter fullscreen mode Exit fullscreen mode

Solution:

# Install unzip before extraction
- name: Ensure unzip is installed
  package:
    name: unzip
    state: present
  become: yes

# For RedHat/CentOS
- name: Install unzip
  yum:
    name: unzip
    state: present
  become: yes

# For Ubuntu/Debian
- name: Install unzip
  apt:
    name: unzip
    state: present
  become: yes
Enter fullscreen mode Exit fullscreen mode

Issue 5: Wrong Ownership After Extraction

Symptom:

-rw-r--r-- 1 root root file.txt  # Should be tony:tony
Enter fullscreen mode Exit fullscreen mode

Solution:

# Set ownership during extraction
- name: Extract with correct ownership
  unarchive:
    src: /usr/src/data/datacenter.zip
    dest: /opt/data/
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    mode: '0655'
  become: yes

# Or fix after extraction
- name: Fix ownership
  file:
    path: /opt/data
    owner: "{{ ansible_user }}"
    group: "{{ ansible_user }}"
    recurse: yes
  become: yes
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 add to ansible.cfg
[defaults]
host_key_checking = False

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

Issue 7: Module Not Found

Symptom:

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

Solution:

# Check module exists
ansible-doc -l | grep archive
ansible-doc -l | grep unarchive

# Update Ansible
sudo yum update -y ansible

# For older versions, use shell commands
- name: Create archive using shell
  shell: tar -czf /opt/backup/data.tar.gz -C /usr/src data/
Enter fullscreen mode Exit fullscreen mode

8. Security Best Practices

🔐 Protecting Your Automation

1. SSH Key Management

# Generate strong SSH key
ssh-keygen -t ed25519 -C "thor-ansible-$(date +%Y%m%d)"

# Use ssh-agent to manage keys
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519

# Copy keys to servers
ssh-copy-id tony@stapp01
ssh-copy-id steve@stapp02
ssh-copy-id banner@stapp03
Enter fullscreen mode Exit fullscreen mode

2. Ansible Vault for Secrets

# Create encrypted file
ansible-vault create group_vars/all/vault.yml

# Content of vault.yml
---
vault_ssh_pass: "Ir0nM@n"
vault_sudo_pass: "Ir0nM@n"

# Use in inventory
[app_servers]
stapp01 ansible_host=stapp01 ansible_user=tony ansible_ssh_pass={{ vault_ssh_pass }}

# Run playbook with vault
ansible-playbook -i inventory playbook.yml --ask-vault-pass
Enter fullscreen mode Exit fullscreen mode

3. Secure File Permissions

# Set secure permissions on sensitive files
- name: Secure inventory file
  file:
    path: /home/thor/ansible/inventory
    mode: '0600'
    owner: thor
    group: thor

- name: Secure playbook files
  file:
    path: /home/thor/ansible/playbook.yml
    mode: '0644'
    owner: thor
    group: thor
Enter fullscreen mode Exit fullscreen mode

4. Ansible Configuration Security

# /etc/ansible/ansible.cfg
[defaults]
host_key_checking = False
timeout = 30
private_key_file = /home/thor/.ssh/id_ed25519
remote_user = thor
ask_pass = False
vault_password_file = /etc/ansible/.vault_pass

[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
Enter fullscreen mode Exit fullscreen mode

5. Auditing and Logging

---
- name: Enable logging
  hosts: localhost
  tasks:
    - name: Configure ansible logging
      lineinfile:
        path: /etc/ansible/ansible.cfg
        regexp: '^log_path'
        line: 'log_path = /var/log/ansible/ansible.log'
      become: yes

    - name: Create log directory
      file:
        path: /var/log/ansible
        state: directory
        mode: '0755'
      become: yes

    - name: Set log rotation
      copy:
        dest: /etc/logrotate.d/ansible
        content: |
          /var/log/ansible/*.log {
              daily
              rotate 30
              compress
              delaycompress
              missingok
              notifempty
              create 0640 thor thor
          }
      become: yes
Enter fullscreen mode Exit fullscreen mode

9. Performance Optimization

⚡ Speed Up Your Automation

1. Use Gather Facts Wisely

---
# Skip gather facts for faster execution
- name: Fast playbook
  hosts: app_servers
  gather_facts: no  # <-- Speeds up execution

# Use gather facts only when needed
- name: Playbook with conditional facts
  hosts: app_servers
  gather_facts: yes
  when: ansible_system == "Linux"
Enter fullscreen mode Exit fullscreen mode

2. Use creates to Avoid Re-running Commands

- name: Extract archive (only if not extracted)
  unarchive:
    src: /usr/src/data/datacenter.zip
    dest: /opt/data/
  args:
    creates: /opt/data/flag_file  # Skip if flag exists
Enter fullscreen mode Exit fullscreen mode

3. Use Async for Long-running Tasks

- name: Extract large archive
  unarchive:
    src: /usr/src/data/large_archive.zip
    dest: /opt/data/
  async: 3600
  poll: 0
  become: yes

- name: Check async status
  async_status:
    jid: "{{ ansible_job_id }}"
  register: job_result
  until: job_result.finished
  retries: 60
  delay: 10
Enter fullscreen mode Exit fullscreen mode

4. Use Strategy Plugins

---
- name: Efficient playbook execution
  hosts: app_servers
  strategy: free  # Run tasks as fast as possible
  gather_facts: no
  tasks:
    - name: Extract archive
      unarchive:
        src: /usr/src/data/datacenter.zip
        dest: /opt/data/
      become: yes
Enter fullscreen mode Exit fullscreen mode

5. Parallel Execution

# Use forks to run in parallel
ansible-playbook -i inventory playbook.yml --forks 10

# Set in ansible.cfg
[defaults]
forks = 20
Enter fullscreen mode Exit fullscreen mode

10. Conclusion

🎯 Key Takeaways

What We've Learned

Module Purpose Key Parameters Best Use Case
Archive Create compressed archives path, dest, format, mode Backups, data packaging
Unarchive Extract compressed archives src, dest, owner, group Deployments, data distribution

Best Practices Summary

  1. Inventory Management

    • Use group/host variables
    • Separate credentials from inventory
    • Use Ansible Vault for secrets
  2. Module Usage

    • Use archive for creating backups
    • Use unarchive for deployments
    • Always set ownership and permissions
    • Use become: yes for system directories
  3. Playbook Design

    • Make playbooks idempotent
    • Include verification tasks
    • Handle errors gracefully
    • Use variables for reusability
  4. Security

    • Use SSH keys instead of passwords
    • Encrypt sensitive data
    • Set proper file permissions
    • Enable logging for auditing
  5. Performance

    • Skip gather facts when possible
    • Use creates for idempotency
    • Increase forks for parallel execution
    • Use async for long tasks

Complete Command Reference

# Archive Creation
ansible -i inventory app_servers -m archive -a "path=/usr/src/data/ dest=/opt/backup/data.tar.gz format=gz"

# Archive Extraction
ansible -i inventory app_servers -m unarchive -a "src=/usr/src/data/datacenter.zip dest=/opt/data/"

# Verification
ansible -i inventory app_servers -m stat -a "path=/opt/backup/data.tar.gz"
ansible -i inventory app_servers -m shell -a "tar -tzf /opt/backup/data.tar.gz"
ansible -i inventory app_servers -m shell -a "ls -la /opt/data/"

# Playbook Execution
ansible-playbook -i inventory playbook.yml
ansible-playbook -i inventory playbook.yml --check  # Dry run
ansible-playbook -i inventory playbook.yml --verbose
Enter fullscreen mode Exit fullscreen mode

🚀 Next Steps

  1. Expand Your Skills

    • Learn about Ansible Roles
    • Explore Ansible Tower/AWX
    • Implement CI/CD with Ansible
  2. Additional Modules to Explore

    • copy - Copy files from controller to hosts
    • synchronize - Rsync-like file synchronization
    • template - Jinja2 templating
    • file - File and directory management
  3. Real-World Applications

    • Application deployment pipelines
    • Configuration management
    • Infrastructure as Code
    • Disaster recovery automation

📚 Resources


🎉 Final Thoughts

Mastering Ansible's archive and unarchive modules is essential for any DevOps engineer. These modules provide the foundation for:

  • Efficient Data Management: Compress and extract files across multiple servers
  • Automated Deployments: Distribute application packages consistently
  • Reliable Backups: Create and store backups automatically
  • Configuration Distribution: Deploy configuration files at scale

The skills you've learned in this guide will serve you well in your DevOps journey. Remember to always test your playbooks in a safe environment before deploying to production, and continuously refine your automation practices.

Top comments (0)