Reputation: 17
A seemingly trivial when condition on a task is not working as expected. The task runs on a host where it should not. I've tried swapping the placement of the loop: and when: statements, which had no effect. The conditional is so simple, and verified via debug, that I feel like I must be missing something obvious.
I'm attempting to consolidate our project's sudo privileges idempotently across the internal development environment, but explicitly not grant sudo access on our bastion host. This is handled as part of the create-project-accounts role, which creates user accounts across our environment, including on the bastion host. I could break account creation and sudo up into separate roles, but I'd prefer to just get the conditional working here and keep it all together if possible.
add_sudoers.yml is called from the role's main.yml via ansible.builtin.include_tasks. The variable project_sudoersd_file is defined in the role's defaults/main.yml as 'project_sudoers'.
---
- name: What's wrong
tags: create_project_accounts, add_sudoers
ansible.builtin.debug:
var: group_names
- name: Remove old style sudoers.d files
tags: create_project_accounts, add_sudoers
become: true
ansible.builtin.file:
path: /etc/sudoers.d/{{ item }}
state: absent
loop:
- citool
- itsectool
- devgroup1
- developer1
- name: Remove existing project sudoers.d file
tags: create_project_accounts, add_sudoers
become: true
ansible.builtin.file:
path: "/etc/sudoers.d/{{ project_sudoersd_file }}"
state: absent
- name: Add lines to project sudoers.d file
tags: create_project_accounts, add_sudoers
become: true
ansible.builtin.lineinfile:
path: "/etc/sudoers.d/{{ project_sudoersd_file }}"
state: present
create: yes
mode: '644'
owner: root
group: root
line: "{{ item }}"
validate: 'visudo -cf %s'
when: ("'bastion' not in group_names")
loop:
- '%devgroup1 ALL=(ALL) ALL'
- 'itsectool ALL=(ALL) ALL'
- 'citool ALL=(ALL) ALL'
I expect the first three tasks (What's wrong, Remove old style sudoers.d, and Remove existing project sudoers.d file) to run and the last task (Add lines to project sudoers.d file) to be skipped when run on the bastion host.
All four tasks are being executed on the bastion, resulting in an /etc/sudoers.d/project_sudoers file that grants sudo privileges on the bastion host.
[root@project-bastionserver sudoers.d]# cat project_sudoers
%devgroup1 ALL=(ALL) ALL
itsectool ALL=(ALL) ALL
citool ALL=(ALL) ALL
[ansible@project-adminserver ansible]$ ansible-playbook plays/project.yml -K --tags add_sudoers --limit project-bastionserver
BECOME password:
</snip>
PLAY [bastion] *******************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************
TASK [create-project-accounts : Add sudoers] *************************************************************************
included: /project/ansible/plays/roles/create-project-accounts/tasks/add-sudoers.yml for project-bastionserver
TASK [create-project-accounts : What's wrong] ************************************************************************
ok: [project-bastionserver] =>
group_names:
- bastion
TASK [create-project-accounts : Remove old style sudoers.d files] ****************************************************
ok: [project-bastionserver] => (item=citool)
ok: [project-bastionserver] => (item=itsectool)
ok: [project-bastionserver] => (item=devgroup1)
ok: [project-bastionserver] => (item=developer1)
TASK [create-project-accounts : Remove existing project sudoers.d file] **********************************************
changed: [project-bastionserver]
TASK [create-project-accounts : Add lines to project sudoers.d file] *************************************************
changed: [project-bastionserver] => (item=%devgroup1 ALL=(ALL) ALL)
changed: [project-bastionserver] => (item=itsectool ALL=(ALL) ALL)
changed: [project-bastionserver] => (item=citool ALL=(ALL) ALL)
</snip>
PLAY RECAP ***********************************************************************************************************
project-bastionserver : ok=6 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Upvotes: 0
Views: 1835
Reputation: 67959
Two notes on using tests in Ansible conditions
when: 'c' in l
causes a well-described syntax error
... It seems that there is a value started with a quote, and the YAML parser is expecting to see the line ended with the same kind of quote. ...
The correct syntax (suggested by the error message) is to quote the whole expression
when: "'c' in l"
You can omit the quotation wrapping and put the expression into a Scalar block. This makes the expression easier to read, e.g.
- debug:
msg: "c is and x is not in the list {{ l }}"
when: >
'c' in l and
'x' not in l
If the expression doesn't start with a quote (either single or double) you don't have to wrap it in quotes. The condition below is correct
when: l is contains 'c'
is
is not needed.For example, given the list
l: [a, b, c]
a) The Jinja test in
- debug:
msg: "{{ item }} is in the list {{ l }}"
loop: [x, y, c]
when: item in l
- debug:
msg: "{{ item }} is not in the list {{ l }}"
loop: [x, y, c]
when: item not in l
give
TASK [debug] *********************************************************************************
skipping: [localhost] => (item=x)
skipping: [localhost] => (item=y)
ok: [localhost] => (item=c) =>
msg: c is in the list ['a', 'b', 'c']
TASK [debug] *********************************************************************************
ok: [localhost] => (item=x) =>
msg: x is not in the list ['a', 'b', 'c']
ok: [localhost] => (item=y) =>
msg: y is not in the list ['a', 'b', 'c']
skipping: [localhost] => (item=c)
b) You can use is
if you want to. The tasks below give the same results
- debug:
msg: "{{ item }} is in the list {{ l }}"
loop: [x, y, c]
when: item is in l
- debug:
msg: "{{ item }} is not in the list {{ l }}"
loop: [x, y, c]
when: item is not in l
c) In Ansible tests, like contains is
is mandatory
- debug:
msg: "{{ item }} is in the list {{ l }}"
loop: [x, y, c]
when: l is contains item
- debug:
msg: "{{ item }} is not in the list {{ l }}"
loop: [x, y, c]
when: l is not contains item
give
TASK [debug] *********************************************************************************
skipping: [localhost] => (item=x)
skipping: [localhost] => (item=y)
ok: [localhost] => (item=c) =>
msg: c is in the list ['a', 'b', 'c']
TASK [debug] *********************************************************************************
ok: [localhost] => (item=x) =>
msg: x is not in the list ['a', 'b', 'c']
ok: [localhost] => (item=y) =>
msg: y is not in the list ['a', 'b', 'c']
skipping: [localhost] => (item=c)
d) In an Ansible test if you omit is
, e.g.
- debug:
msg: "{{ item }} is in the list {{ l }}"
loop: [x, y, c]
when: l contains item
the task will fail
The error was: template error while templating string: expected token 'end of statement block', got 'contains'.
Upvotes: 0