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.
Unzip
/usr/src/data/datacenter.ziparchive in/opt/data/location on all app servers.Make sure the extracted data must has the respective sudo user as their
userandgroupowner, i.e tony for app server 1, steve for app server 2, banner for app server 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
archiveandunarchivein 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
- Understanding the Modules
- Inventory File Best Practices
- The Archive Module: Creating Archives
- The Unarchive Module: Extracting Archives
- Real-World Implementation
- Verification and Testing
- Troubleshooting Guide
- Security Best Practices
- Performance Optimization
- 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'
📂 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'
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
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!
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
🔑 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
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 }}
3. Use Separate Variable Files
ansible/
├── inventory/
│ └── production
├── group_vars/
│ └── app_servers.yml
└── host_vars/
├── stapp01.yml
├── stapp02.yml
└── stapp03.yml
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
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
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
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 }}"
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
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
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 }}"
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 }}"
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
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!
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
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
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"
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'"
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!"
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 }}"
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"
}
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"
Issue 2: Permission Denied
Symptom:
fatal: [stapp01]: FAILED! => {
"msg": "Permission denied"
}
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"
Issue 3: Archive Not in Correct Format
Symptom:
gzip: stdin: not in gzip format
tar: Child returned status 1
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
Issue 4: Unzip Not Installed
Symptom:
fatal: [stapp01]: FAILED! => {
"msg": "unzip is required for unarchive module"
}
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
Issue 5: Wrong Ownership After Extraction
Symptom:
-rw-r--r-- 1 root root file.txt # Should be tony:tony
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
Issue 6: Host Key Verification Failed
Symptom:
Host key verification failed
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
Issue 7: Module Not Found
Symptom:
ERROR! no action detected in task
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/
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
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
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
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
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
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"
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
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
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
5. Parallel Execution
# Use forks to run in parallel
ansible-playbook -i inventory playbook.yml --forks 10
# Set in ansible.cfg
[defaults]
forks = 20
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
-
Inventory Management
- Use group/host variables
- Separate credentials from inventory
- Use Ansible Vault for secrets
-
Module Usage
- Use
archivefor creating backups - Use
unarchivefor deployments - Always set ownership and permissions
- Use
become: yesfor system directories
- Use
-
Playbook Design
- Make playbooks idempotent
- Include verification tasks
- Handle errors gracefully
- Use variables for reusability
-
Security
- Use SSH keys instead of passwords
- Encrypt sensitive data
- Set proper file permissions
- Enable logging for auditing
-
Performance
- Skip gather facts when possible
- Use
createsfor 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
🚀 Next Steps
-
Expand Your Skills
- Learn about Ansible Roles
- Explore Ansible Tower/AWX
- Implement CI/CD with Ansible
-
Additional Modules to Explore
-
copy- Copy files from controller to hosts -
synchronize- Rsync-like file synchronization -
template- Jinja2 templating -
file- File and directory management
-
-
Real-World Applications
- Application deployment pipelines
- Configuration management
- Infrastructure as Code
- Disaster recovery automation
📚 Resources
- Ansible Documentation: https://docs.ansible.com/
- Module Index: https://docs.ansible.com/ansible/latest/collections/index.html
- Best Practices: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_best_practices.html
- Ansible Galaxy: https://galaxy.ansible.com/
🎉 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)