Where is Ansible variables in Playbook located - ansible

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]}'

Related

ansible-playbooks - install a list of apt packages from a file

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

unpacking yaml values in the ansible playbook

i'm a fresher in the professional world as i just joined amid corona situation as being work from i have been involved to understand and write ansible codes and at some level i am growing up by watching through SO posts to get variety of ticks & tricks.
I have the below ansible playbooks..
1- One is custom_pkgs.yml which basically installing some custom build packages using yum command where it calling a pkgs.yml file which lists the packages to be install.
I somewhat understood the code but
$ vi custom_pkgs.yml
---
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Install license software
shell: "yum install -y {{ license[ item ] | join(' ') }}"
with_items: "{{ license }}"
changed_when: True
when: item != "remove"
- name: Remove any unwanted RPMS
shell: "yum remove -y {{ license.remove | join(' ') }}"
changed_when: True
when: license.remove is defined
...
Below is the pkg.yml
$ cat pkg.yml
---
license:
- fenixlmd.noarch
- tmpwatch
- xorg-x11-deprecated-libs.i386
- Tasking.noarch
- rotate_fix.noarch
- plexim.noarch
- interrad.noarch
- idsd.noarch
- gsi.noarch
- java-1.8.0-openjdk
- java-1.8.0-openjdk-devel
- java-1.8.0-openjdk-debug
- flexnet_agent
- magillem.noarch
- redhat-lsb-printing
- redhat-lsb-printing.i686
- redhat-lsb-core
- redhat-lsb-core.i686
- redhat-lsb
- redhat-lsb.i686
- git
- gcc
- python-devel
...
What i would like to Know:
I am trying to understand about below two lines..
shell: "yum install -y {{ license[ item ] | join(' ') }}" and when: item != "remove"
I have went through all the basics of asking question in SO in case i've ask something out of way i would like to be excused as this is my first post.
Regards ..
Thanks for learning Ansible, this is an old Ansible code. The pkg.yml file defines a list variable called licence containing a list of yum package to install on the remote host using the Ansible shell module. with_items: "{{ license }}" tell the module to iterate on that variable items.
The When condition help to skip the package when his name is remove.
Read the Ansible conditional documentation: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#the-when-statement
But as i told, it's an old code. On Ansible recent version, you will use a yum module to install yum package (create two lists of package: package_to_install and package_to_remove
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Install license software
yum:
name: "{{ package_to_install }}"
changed_when: True
when: item != "remove"
- name: Remove any unwanted RPMS
yum:
name: "{{ package_to_remove }}"
state: absent
changed_when: True
Make sure to use a recent Ansible version (2.9).
Read the yum module documentation: https://docs.ansible.com/ansible/latest/modules/yum_module.html
You can Also defined only one list of package variable with two fields:
license:
- { name: fenixlmd.noarch, state: present }
- { name: tmpwatch, state: absent }
- { name: xorg-x11-deprecated-libs.i386, state: present }
.
.
.
And then use the yum module like that:
- name: Install License
hosts: all
become: yes
become_user: root
become_method: sudo
tasks:
- name: Include the variables to install the license software
include_vars:
file: "vars/pkg.yml"
name: license
- name: Remove any unwanted or install needs package RPMS
yum:
name: "{{ item.name }}"
state: "{{ item.state }}"
with_items: "{{ licence }}"

package installation not considered in playbook

I got some trouble with automating an installation using ansible.
I use this role (https://github.com/elastic/ansible-elasticsearch) to install elasticsearch on my ubuntu 16.04 server.
The role depends on the package python-jmespath, as mentioned in the documentation.
The role DOES NOT install the package itsself, so i try to install it before role execution.
- hosts: elasticsearch_master_servers
become: yes
tasks:
- name: preinstall jmespath
command: "apt-get install python-jmespath"
- name: Run the equivalent of "apt-get update" as a separate step
apt:
update_cache: yes
- hosts: elasticsearch_master_servers
become: yes
roles:
- role: elastic.elasticsearch
vars:
...
When running the playbook i expect the python-jmespath package to be installed before execuction of role takes place, but role execution fails with
You need to install \"jmespath\" prior to running json_query filter"
When i check if the package is installed manually using dpkg -s python-jmespath i can see the package is installed correctly.
A second run of the playbook (with the package already installed) doesnt fail.
Do I miss an ansible configuration, that updates the list of installed packages during the playbook run ?
Am I doing something wrong in general ?
Thanks in advance
FWIW. It's possible to tag installation tasks and install the packages in the first step. For example
- name: install packages
package:
name: "{{ item.name }}"
state: "{{ item.state|default('present') }}"
state: present
loop: "{{ packages_needed_by_this_role }}"
tags: manage_packages
Install packages first
shell> ansible_playbook my-playbook.yml -t manage_packages
and then run the playbook
shell> ansible_playbook my-playbook.yml
Notes
This approach makes checking of the playbooks with "--check" much easier.
Checking idempotency is also easier.
With tags: [manage_packages, never] the package task will be skipped when not explicitly selected. This will speed up the playbook.

Installing multiple packages in Ansible

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.

How to do best organization of install and delete in the same ansible playbook?

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.

Resources