I have the following file structure:
webserver.yml
emailserver.yml
roles/
common/
tasks/
main.yml
emailserver.yml and webserver.yml look like:
- hosts: all
roles:
- common
And main.yml:
- name: apt-get update
apt:
update_cache: yes
force_apt_get: yes
- name: apt-get upgrade
apt:
name: "*"
state: latest
force_apt_get: yes
In order to avoid repeating force_apt_get: yes, I would like to use the module_defaults attribute for this role:
module_defaults:
apt:
force_apt_get: yes
I've tried to include that at the top of main.yml but I'm getting a syntax error.
How can I apply a module_defaults attribute to a role's tasks?
(an alternative is to add module_defaults to both webserver.yml and emailserver.yml, but it means code duplication and having the common role configuration spread over a number of files).
One solution is to use a block in the role task file, main.yml:
- block:
- name: apt-get update
apt:
update_cache: yes
- name: apt-get upgrade
apt:
name: "*"
state: latest
become: yes
module_defaults:
apt:
force_apt_get: yes
How can I apply a module_defaults attribute to a role?
It is not possible as of current Ansible version (2.7). module_defaults is a Play attribute only.
Related
I'm trying create a Ansible playbook that will read contents of a file and use those contents to install packages on a target machine.
In simpler terms, I want to run this command converted to an ansible playbook
cat ./meta/install-list/apt | xargs apt install -y
./meta/install-list/apt
neofetch
tmux
git
./ansible/playbooks/apt.yaml
- hosts: all
become: true
tasks:
- name: Extract APT packages to install
command: cat ../../meta/install-list/apt
register: _pkgs
delegate_to: localhost
run_once: true
- name: Install APT packages
apt:
name: "{{ _pkgs.stdout_lines }}"
state: latest
./ansible.cfg
[defaults]
inventory = ./ansible/inventory/hosts.yaml
./ansible/inventory/hosts.yaml
---
all:
children:
group-machines:
hosts:
target-machine.local
Command to run playbook
ansible-playbook --ask-become-pass ./ansible/playbooks/apt.yaml --limit group-machine
When running the command, it gets stuck on Extract APT packages to install
NOTE:
these files mentioned above are to be only on machine that is running the command. If possible, I'd like to prevent copying files to target machines and then running the playbooks tasks
PS: new to ansible
I don't see anything in your "Extract APT packages to install" task that should cause it to get stuck... but you don't need that task in any case; you can combine your two tasks into a single task like this:
- hosts: all
become: true
tasks:
- name: Install APT packages
apt:
name: "{{ packages }}"
state: latest
vars:
packages: "{{ lookup('file', '../../meta/install-list/apt').splitlines() }}"
Here we're using a file lookup to read the contents of a file. Lookups always run on the local (control) host.
Note that you could write the above like this as well...
- hosts: all
become: true
tasks:
- name: Install APT packages
apt:
name: "{{ lookup('file', '../../meta/install-list/apt').splitlines() }}"
state: latest
...but I like to keep longer jinja expressions in vars in order to keep the rest of the arguments more readable.
The above answer is more than enough by #Zeitounator. But if you do some formatting to your original file of package list as below
packages:
- neofetch
- tmux
- git
After that you can simply run the playbook like below
- hosts: all
become: true
vars_files: ../../meta/install-list/apt
tasks:
- name: Install APT packages
apt:
name: "{{ packages }}"
state: latest
Now suppose if you are lazy enough to not want to do the formatting then below playbook also will do the trick. Its much cleaner and scalable in my opinion.
---
- name: SHow the packages list
hosts: localhost
become: true
tasks:
- name: View the packages list file
shell: cat ../../meta/install-list/apt
register: output
- name: Install the package
apt:
name: "{{ output.stdout_lines }}"
state: latest
I have this task in Ansible:
- name: Install mongodb
yum:
name:
- "mongodb-org-{{ mongodb_version }}"
- "mongodb-org-server-{{ mongodb_version }}"
- "mongodb-org-mongos-{{ mongodb_version }}"
- "mongodb-org-shell-{{ mongodb_version }}"
- "mongodb-org-tools-{{ mongodb_version }}"
state: present
notify: Restart mongodb
Is there a way I can indicate the version without having to use a loop like this? What is a more "elegant" way of writing this?
- name: Install mongodb
yum:
name: "{{ item }}-{{ mongodb_version }}"
state: present
loop:
- mongodb-org-server
- mongodb-org-mongos
- mongodb-org-shell
- mongodb-org-tools
notify: Restart mongodb
To my surprise I didn't find the simplest solution in all the answers, so here it is. Referring to the question title Installing multiple packages in Ansible this is (using the yum module):
- name: Install MongoDB
yum:
name:
- mongodb-org-server
- mongodb-org-mongos
- mongodb-org-shell
- mongodb-org-tools
state: latest
update_cache: true
Or with the apt module:
- name: Install MongoDB
apt:
pkg:
- mongodb-org-server
- mongodb-org-mongos
- mongodb-org-shell
- mongodb-org-tools
state: latest
update_cache: true
Both modules support inline lists!
The second part of your question is how to integrate specific version numbers into the package lists. No problem - simply remove the state: latest (since using specific version numbers together with state: latest would raise errors) and add the version numbers to the package names using a preceding = like this:
- name: Install MongoDB
yum:
name:
- mongodb-org-server=1:3.6.3-0centos1.1
- mongodb-org-mongos=1:3.6.3-0centos1.1
- mongodb-org-shell=1:3.6.3-0centos1.1
- mongodb-org-tools=1:3.6.3-0centos1.1
update_cache: true
You could also optimize further and template the version numbers. In this case don't forget to add quotation marks :)
Make this into an Ansible Role called mongo, resulting in the directory structure:
playbook.yml
roles
|-- mongo
| |-- defaults
| | |-- main.yml
| |
| |-- tasks
| | |-- main.yml
| |
| |-- handlers
| | |-- main.yml
Add the required MongoDB packages and version into the default variables file roles/mongo/defaults/main.yml:
mongo_version: 4.0
mongo_packages:
- mongodb-org-server
- mongodb-org-mongos
- mongodb-org-shell
- mongodb-org-tools
Re-write the task in the roles/mongo/tasks/main.yml file:
- name: Install mongodb
yum:
name: "{{ item }}-{{ mongo_version }}"
state: present
with_items: "{{ mongo_packages }}"
notify: Restart mongodb
Add your handler logic to restart MongoDB in the file roles/mongo/handlers/main.yml.
Write a Playbook called playbook.yml that calls the role.
---
- hosts: all
roles:
- mongo
Some people have mentioned syntax that allows passing a list, but you don't need to special-case your code for apt or yum. Here is the command that uses the generic package manager module:
- name: Install packages
become: yes
package:
name:
- tmux
- rsync
- zsh
- rsync
- wget
state: present
(Or state: latest.) This feature is only documented for Ansible 2.9, so it may be new.
I think this is best way, but as the package names should not be hard coded, it is preferred to keep in vars/main.yml
e.g
mongodb_version: 5
packages:
- "mongodb-org-shell-{{ mongodb_version }}"
- "mongodb-org-server-{{ mongodb_version }}"
Call this inside your playbook as
- name: Install mongodb packages
yum: name={{ item }}
state=latest
with_items: "{{ packages}}"
Now yum module direct accept array of packages like this
vars:
pkgs:
- screen
- rubygems
- ksh
- strace
tasks:
- name: Install pre-requisite packages
yum: name={{ pkgs }} state=installed
A more elegant way, which doesn't keep re-checking each and every package if installed every time the script is run is to use something like this:
In vars/main.yml or similar:
packages_to_install:
- mongodb-org-server
- mongodb-org-mongos
- mongodb-org-shell
- mongodb-org-tools
In tasks/main.yml or similar:
- name: "Get installed packages"
yum:
list: "installed"
register: installed_packages
- name: "Install missing packages"
package:
state: "present"
name: "{{ item }}"
with_items: "{{ packages_to_install | difference(installed_packages | json_query('results[*].name')) }}"
Essentially this checks what is already installed and only installs missing packages. Counter-intuitively this is faster. You can also use dnf instead of yum.
This is just an example I found on Github:
- name: Install dependencies
apt:
name: '{{ packages }}'
state: latest
update_cache: yes
vars:
packages:
- curl
- software-properties-common
- python3-pip
become: yes
My question is:
where is the "{{ packages }}" variable usually declared?
Variables are typically declared in a group_vars or host_vars folder (see the docs on "Best practices"). But in your example, the variable is declared in the task itself. "{{ packages }}" is a list of three packages:
vars:
packages:
- curl
- software-properties-common
- python3-pip
You can also pass it as an argument while running the playbook.
For example:
This is your playbook
- name: Install dependencies
apt:
name: '{{ packages }}'
state: latest
update_cache: yes
Then you can call your playbook with those params
ansible-playbook installer.yml --extra-vars '{"packages":[curl, python3-pip]}'
While installing pkgs Ansible fails, because there is a need to accept licensing terms.
How to auto accept terms through ansible-playbook?
---
- hosts: client1
remote_user: ansible
become: True
tasks:
- name: testing
apt_repository: repo=ppa:webupd8team/java state=present
- name: updating
apt: update_cache=yes
- name: installaing oracle pkg
apt: pkg=oracle-java8-installer state=present update_cache=yes
There is no universal method for "packages".
For Oracle Java add a task before calling apt:
- debconf:
name: oracle-java8-installer
question: shared/accepted-oracle-license-v1-1
value: true
vtype: select
For virtualbox-ext-pack
- debconf:
name: virtualbox-ext-pack
question: virtualbox-ext-pack/license
value: "true"
vtype: select
before apt install command.
I try to figure how I must to construct an ansible playbooks where I can do some action and undo them (I can install or remove same packages; place file or remove this file).
If I create two ansible playbooks: delete.yml and install.yml. There are may be problem's like:
I added to installation someting, but don't change deletion
Example:
install.yml:
---
- name: Add x2go repository
apt_repository: repo='deb http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
apt_repository: repo='deb-src http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
- name: Install x2go application
apt: name=x2goserver update_cache=yes state=present
apt: name=x2goserver-xsession update_cache=no state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
delete.yml:
---
- name: Add x2go repository
apt_repository: repo='deb http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=absent
apt_repository: repo='deb-src http://ppa.launchpad.net/x2go/stable/ubuntu precise main' state=present
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
- name: Install x2go application
apt: name=x2goserver update_cache=yes state=absent
apt: name=x2goserver-xsession update_cache=no state=absent
when: ansible_os_family == "Debian"
tags:
- remote-access-x2go
This is a very interesting idea. I have personally never tried the 'undoing' workflow, but I can see the nice things about this idea and would like to use it sometime. Here is what I would do.
In my ansible-role/defaults/main.yml I would define a variable flag
# defaults file for ansible-role
flag_undo: false
In my ansible-role/tasks/main.yml I would have
- name: task foo bar
command: falana dhimaka
- name: undoing task foo bar
command: undo falana dhimaka
when: flag_undo=true
So by default our flag is always false. So when installing things I would us the first command below to run my plays. And to uninstall I would use the second command.
ansible-playbook foo-play.yml
ansible-playbook foo-play.yml --extra-vars "flag_undo=true"
One approach that I use in some cases is to simply have lists of packages that you want installed and lists you want removed, then iterate over each list. I use this basic method not only for packages but other things as well, like users, groups, etc. For example, I have a "packages" role that has the following files in it:
vars/main.yml:
---
installed_system_packages:
- telnet
- screen
- postfix
latest_system_packages:
- glibc
removed_packages:
- sendmail
tasks/main.yml:
---
- name: Install system packages (latest)
yum: pkg={{ item }} state=latest
with_items: latest_system_packages
- name: Install system packages
action: yum pkg={{ item }} state=installed
with_items: installed_system_packages
- name: Remove unwanted packages
action: yum pkg={{ item }} state=removed
with_items: removed_packages
This way, if I decide that I no longer want a package like telnet installed I can just move it from installed_system_packages to removed_packages. Or if I want to ensure I'm running the latest version of screen I would simply move it to the latest_system_packages list. Then it's just a matter of re-running the role to have the changes applied.