Bootstrap FreeKB - Ansible - Extract a tar zip bzip2 gzip archive using the unarchive module
Ansible - Extract a tar zip bzip2 gzip archive using the unarchive module

Updated:   |  Ansible articles

If you are not familiar with modules, check out Ansible - Getting Started with Modules.

The unarchive module is used to extract an archive, such as a .zip file or a tar archive. For example, this playbook would extract foo.zip to the /tmp directory. By default, unarchive will look for the src file on your control node (that's your Ansible server) and extract to a directory on the target servers.

---
- hosts: all
  tasks:
  - name: extract /path/to/foo.zip
    ansible.builtin.unarchive:
      src: /path/to/foo.zip
      dest: /tmp
...

 

If the destination directory does not exist on the managed node, something like this will be displayed.

TASK [unarchive] 
fatal: [server1.example.com]: FAILED! => {"changed": false, "msg": "dest '/tmp/bogus' must be an existing dir"}

 

For this reason, you may want to use the file module to create the dest directory before using the unarchive module.

---
- hosts: all
  tasks:
  - name: mkdir /tmp/example
    ansible.builtin.file:
      path: /tmp/example
      state: directory
...

 

You may want to include register so that you can use debug to see the result.

---
- hosts: all
  tasks:
  - name: extract /path/to/foo.zip
    ansible.builtin.unarchive:
      src: /path/to/foo.zip
      dest: /tmp
    register: out

  - ansible.builtin.debug:
      var: out
...

 

Something like this should be returned. When "changed" is "false", this means the file was NOT extracted.

ok: [server1.example.com] => {
    "msg": {
        "changed": false, 
        "dest": "/tmp", 
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "handler": "ZipArchive", 
        "mode": "01777", 
        "owner": "root", 
        "secontext": "system_u:object_r:tmp_t:s0", 
        "size": 8192, 
        "src": "/tmp/foo.zip", 
        "state": "directory", 
        "uid": 0
    }
}

 

When "changed" is "true", this means the file was extracted.

changed: [server1.example.com] => {
    "msg": {
        "changed": true, 
        "dest": "/tmp", 
        "diff": {
        }
        "extract_results": {
            "cmd": [
                "/bin/unzip", 
                "-o", 
                "/tmp/foo.zip", 
                "-d", 
                "/tmp"
            ], 
            "err": "", 
            "out": "Archive:  /tmp/foo.zip\n  inflating: /tmp/path/to/foo.txt  \n  inflating: /tmp/path/to/bar.txt
        }
        "failed": false, 
        "gid": 0, 
        "group": "root", 
        "handler": "ZipArchive", 
        "mode": "01777", 
        "owner": "root", 
        "secontext": "system_u:object_r:tmp_t:s0", 
        "size": 8192, 
        "src": "/tmp/foo.zip", 
        "state": "directory", 
        "uid": 0
    }
}

 

remote_src can be used if the file you want to extract resides on the target server.

---
- hosts: all
  tasks:
  - name: extract /path/to/foo.zip
    ansible.builtin.unarchive:
      src: /path/to/foo.zip
      dest: /tmp
      remote_src: true
...

 

 

AVOID TROUBLE

If you attempt to extract an archive that contains no files (an empty archive), the following fatal error will be returned.

fatal: [server1.example.com]: FAILED! => {
  "changed": false,
  "msg": "Failed to find handler for \\"foo.tar.gz\\". Make sure the required command to extract the file is installed. Command \\"/bin/gtar\\" found no files in archive. Command \\"/bin/gtar\\" could not handle archive. Command \\"/bin/unzip\\" could not handle archive."
}

 

To account for this situation, you may want to use the shell module to first determine if the archive is empty. Then, you can apply when: out.stdout != '' to the unarchive task.

- name: check to see if the archive is empty
  ansible.builtin.shell: "tar -ztf foo.tar.gz"
  register: out
  failed_when: out.rc not in [ 0, 1 ]

 

If the file is successfully unarchived, something like this should be displayed.

TASK [unarchive]
changed: [server1.example.com]

 

list_files can be used to list the files in the archive. Check out my article List the files and directories in a tar zip bzip2 gzip archive using the unarchive module.

include can be used to only extract specific files from the archive. Check out my article Extract specific files in a tar zip bzip2 gzip archive using the unarchive module

 

unzip

When attempting to unarchive a .zip file, the unzip package must exists on the managed node. If the unzip package is not installed on the managed node, something like this should be displayed. For this reason, you may want to use the yum module to install the unzip package on the managed node before using the unarchive module.

TASK [unarchive] 
fatal: [server1.example.com]: FAILED! => {"changed": false, "msg": "Failed to find handler for \"/root/.ansible/tmp/ansible-tmp-1609665721.5095057-23328-132110161264122/source\". Make sure the required command to extract the file is installed. Command \"unzip\" not found. Command \"/usr/bin/gtar\" could not handle archive."}

 

The extra_opts parameter can be used to include options supported by the command being used. For example, --add-file is an option supported by the tar command. In this example, only usr/local/foo.txt will be extracted.

- name: extract only foo.txt from foo.zip
  ansible.builtin.unarchive:
    src: /path/to/foo.zip
    dest: /
    extra_opts:
      - "--add-file"
      - "usr/local/foo.txt"
  register: out

 

roles

If using the unarchive module in a role (see Ansible - Getting Started with Roles), the file being unarchived can be placed in the roles files directory.

roles/foo/files/foo.zip

 

Then, src would just be the name of the file that is being unarchived.

- name: extract foo.zip to /tmp
  ansible.builtin.unarchive:
    src: foo.zip
    dest: /tmp

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter 1dc161 in the box below so that we can be sure you are a human.