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