ansible apt-key module with loop - loops

I'm provisioning a system that requires multiple GPG keys to be added. I'm attempting to streamline the process and follow DRY principals.
I have apt packages installing from a vars list like so:
- name: Install packages
apt: name={{ apt_packages }}
Where my vars.yml looks like this:
apt_packages:
- tilix
- terraform
- ansible
- opera
This works because the apt module accepts comma separated inputs and parses accordingly.
So I'm trying to achieve a similar process when using the apt_key module but I can't seem to get it to work. Here are a couple of attempts I've made:
- name Add keys
apt_key:
url: url="{{ items }}"
loop: "{{ gpg_keys }}"
state: present
and
- name: Add GPG Keys
apt_key:
url: url="{{ gpg_keys }}"
state: present
Both throw different errors.
Is it possible to do something like this using the apt-key module? Obviously I'm trying to avoid having a separate caller for each key I want to add as there will be many keys and I'd like to be able to add additional keys later on by simply appending the list in vars.yml.

You have a few small mistakes in your task.
The right way is this:
- name: Add keys
apt_key:
url: "{{ item }}"
state: present
loop: "{{ gpg_keys }}"
you already have the key url, so prepending url= is incorrect
loop is an argument to the task and not to the apt_key module, so it needs to be indented to the level of apt_key (unlike url which is an argument to the model)
Sidenotes:
You also need to make sure that gpg_keys contains a list, similar to apt_packages.
The name parameter of apt accepts a list, as you define correctly in your vars.yml, no comma-separated string. (You are already doing it right)
Documentation:
apt
apt_key

Related

How to loop an array of packages in an Ansible role

I have made role for installing php5-fpm (with other roles: nginx, worldpress, mysql). I want to install php5 set of packages, but have problem with the looping an array of packages. Please some tips how to solve this issue.
Role php5-fpm include:
roles/default/main.yml
roles/tasks/install.yml
default/main.yml:
---
# defaults file for php-fpm
# filename: roles/php5-fpm/defaults/main.yml
#
php5:
packages:
- php5-fpm
- php5-common
- php5-curl
- php5-mysql
- php5-cli
- php5-gd
- php5-mcrypt
- php5-suhosin
- php5-memcache
service:
name: php5-fpm
tasks/install.yml:
# filename: roles/php5-fpm/tasks/install.yml
#
- name: install php5-fpm and family
apt:
name: "{{ item }}"
with_items: php5.packages
notify:
- restart php5-fpm service
I want that "with_items" from install.yml look into defaults/main.yml and take that array of packages
Expand the variable
wrong
with_items: php5.packages
correct
loop: "{{ php5.packages }}"
Quoting from Loops
We added loop in Ansible 2.5. It is not yet a full replacement for with_, but we recommend it for most use cases.
We have not deprecated the use of with_ - that syntax will still be valid for the foreseeable future.
We are looking to improve loop syntax - watch this page and the changelog for updates.

ansible register with loops

I want to create user and gather their info in local file but with loop register is now working as expected.
I thought it was an indentation problem but no luck.
My playbook
---
- hosts: localhost
tasks:
- name: Clearing Local file
local_action: shell echo "Zone,docode,doname,testuser Output" > user.csv
- hosts: app
tasks:
- name: Creating user Testuser
become: yes
user:
name: "{{ item }}"
state: present
shell: "/bin/bash"
password: "$6$mysecretsalt$qyctTVhRMS1ZSnCuzQNAM8Y7V/yqSEnyRbal0IYXSqSEVKkXF8ZmXBZoRIaN/PvzE/msq8iOJO830OOCG89va/"
update_password: always
groups: santosh
shell: id "{{item}}"
ragister: userout
loop:
- newuser1
- newuser2
- newuser3
- debug:
var=userout
which gives the following error when executed
ERROR! conflicting action statements: shell, user
The error appears to have been in '/home/santosh/ans-home/playbooks/Create_User_and_Gather_output.yml': line 12, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Creating user Testuser
^ here
You cannot call several modules in one task, you need to separate each call in its own task, as reported by the error message.
I understand why you tried it: to take advantage of the same loop for several task. Unfortunately this is not possible. You could move your set of tasks to a separate file and include it in a loop if you really have to loop over a significant amount of tasks. This is not really required in your situation because:
you only have two tasks
you can compact writing of your loop by using a declared var for reuse
and most essentially because you don't need your second task
In fact, the user module will return the uid of the user it created or that is existing in its result. You just have to register the result of calling the user module.
Just try the following for your second play:
- name: Resgister application users
hosts: app
vars:
user_list:
- newuser1
- newuser2
- newuser3
tasks:
- name: Create the users if they don't exist
user:
name: "{{ item }}"
state: present
shell: "/bin/bash"
password: "$6$mysecretsalt$qyctTVhRMS1ZSnCuzQNAM8Y7V/yqSEnyRbal0IYXSqSEVKkXF8ZmXBZoRIaN/PvzE/msq8iOJO830OOCG89va/"
update_password: always
groups: santosh
register: create_users
loop: "{{ user_list }}"
- name: Show ids of users
debug:
msg: "The uid of user {{ item.name }} is: {{ item.uid }}"
loop: "{{ create_users.results }}"
And as a side note: for your first play, do yourself a favor and stop using the old local_action syntax in favor of delegate_to: localhost for a task. It is not even required in your case as your are already targeting your play to localhost only.

Ansible check if key in complex dictionnary

I have complex dictionary structure and I want to check if a subkey is defined. I don't even know if this is possible..
Here's what my dict looks like:
config:
1:
client: ubuntu
network_setup:
- routed
ports:
sw1: null
sw2:
client: 1/0/2
lte: 1/0/5
2:
client: archlinux
network_setup:
- bridged
ports:
sw1:
client: 1/0/4
sw2: null
...
Note: lte key could not be defined!
What I want is to check if lte is defined in the config dict. Ideally I would need to loop over each entry in config using a loop.
I will maybe wrote a custom plugin because this sounds really hard to do..
if it's a list, you'll have to loop over it and you can try this condition
- debug: var=test_item.ports.sw2.lte
when: test_item.ports.sw2.lte is defined
with_items: "{{ config }}"
loop_control:
loop_var: test_item
the when condition would check if the variable is defined.

ansible loop over list and dictionary at the same time

I am writing a playbook that ensure nodes appear in /etc/fstab.
I am using loops to prevent code duplication.
The logic is first to check if the line appears using grep (with perl regex because it is a multi line) and store the results in a register.
Then I want to add only the lines that are not in fstab file. To achieve that I need to loop over list (the register with the grep return codes) and a dictionary (that contains the fstab entries).
I am having errors with the parallel loop. I tried to follow these steps.
One or more undefined variables: 'str object' has no attribute 'item'
tasks/fstab.yaml:
---
- name: Make dirs
sudo: yes
file: path={{ item.value }} state=directory
with_dict:
"{{ fstab.paths }}"
- name: Check whether declared in fstab
sudo: no
command: grep -Pzq '{{ item.value }}' /etc/fstab
register: is_declared
with_dict:
"{{ fstab.regexs }}"
- name: Add the missing entries
sudo: yes
lineinfile: dest=/etc/fstab line="{{ item.1.item.value }}"
when: item.0.rc == 1
with_together:
- "{{ is_declared.results }}"
- "{{ fstab.entries }}"
vars/main.yml:
---
fstab:
paths:
a: "/mnt/a"
b: "/mnt/b"
regexs:
a: '\n# \(a\)\nfoo1'
b: '\n# \(b\)\nfoo2'
entries:
a: "\n# (a)\nfoo1"
b: "\n# (b)\nfoo2"
I am not using template on purpose (I want to add entries to existing files and not to over write them).
UPDATE: I see ansible has module "mount" which deals with fstab. However I am still looking for a solution to this issue because I might be needed it again later on.
I have a couple ideas as to why your original approach was failing, but let's scratch that for a moment. It looks like you're overcomplicating things- why not use a complex list var to tie it all together, and use the regexp arg to the lineinfile module instead of a separate regex task? (though your sample data should work fine even without the regexp param) Something like:
---
- name: Make dirs
sudo: yes
file: path={{ item.path }} state=directory
with_items: fstab
- name: Add the missing entries
sudo: yes
lineinfile: dest=/etc/fstab line={{ item.entry }} regexp={{ item.regex }}
with_items: fstab
fstab:
- path: /mnt/a
regex: '\n# \(a\)\nfoo1'
entry: "\n# (a)\nfoo1"
- path: /mnt/b
regex: '\n# \(b\)\nfoo2'
entry: '\n# (b)\nfoo2'

Ansible create directories from a list

I want to create some directories from a list I have in my vars/main.yml.
- app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
My tasks/main.yml so far has this:
- name: Create application directory structure
file:
path: "{{ item }}"
state: directory
mode: 755
with_items:
- app_root
but doesn't work. I thought this could be achieved using with_dict and I also tried:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict:
- app_root
but I got: fatal: [vagrant.dev] => with_dict expects a dict.
I've read all about looping-over-hashes, but this doesn't seem to work.
The reason I'm using this notation is because I use these variables elsewhere as well and I need to know how to call them.
I personally find it a bit easier to convert yaml to json to make sure I'm understanding it properly. Take your example:
- app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
What you have here is not a list, but a nested dictionary. If you converted this to json it would look like this:
[
{
"app_root": {
"network_prod": "/var/www/prod/network/app",
"database_prod": "/var/www/prod/db/app"
}
}
]
In order to loop through this in Ansible you would need to dereference two levels of a dictionary, the first being app_root and the second being the path elements. Unfortunately I don't think Ansible supports looping through nested dictionaries, only through nested loops.
Your best bet is probably to redo the way you're defining your paths so that you're not creating as complex a data structure. If all you're doing in this case is iterating over a list of paths in order to ensure the directories exist then I'd suggest something like this in your vars/main.yml file:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
app_root:
- network_prod
- database_prod
Then you can have a task like this:
file: path={{ item }}
state=directory
with_items: app_root
In vars/main.yml, try removing the dash in front of app_root.
app_root:
network_prod: "/var/www/prod/network/app"
database_prod: "/var/www/prod/db/app"
I think that the approach with with_dict was correct and I believe that the only issue here is the dash - in front of app_root variable. Instead of:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict:
- app_root
It should be:
- name: Create application directory structure
file:
path: "{{ item.value }}"
state: directory
mode: 755
with_dict: app_root
See the difference on the way the variable app_root is passed to with_dict.
A dash in YAML starts a list and the elements are not treated as variables but as literals, think of it as if you were passing an immutable string 'app_root' to with_dict (not exactly true but it helps me to think it this way) so when_dict fails to parse it because it is given a list instead of the expected dictionary. However, without a dash, with_dict is populated with the variable app_root instead and it will parse it without issues.

Resources