Anisble: get dynamic register from with_items - ansible

I've tried to create a register using with_items that can dynamically change with any added item.
- name: Set Openfile for specific user in /etc/security/limits.d/{{ user }}
copy:
dest: /etc/security/limits.d/"{{ item }}"
content: |
"{{ item }}" - nofile 128000
"{{ item }}" - nproc 65536
with_items:
- kube
register: ?
In the code above, I wanted to create a file in /etc/security/limits.d/{{user in with_items}} and store that value in register. So in this case, value in item will be 'kube'.
- name: set permission and change owner of /etc/security/limits.d/{{ user }}
file:
path: /etc/security/limits.d/"{{ user }}"
owner: root
group: root
mode: '0644'
Later, I want to change the directory permissions I've created so it will be /etc/security/limits.d/kube
How can I specify path in file module from the earlier register got from with_items in the copy module above?
Thanks

- name: Set Openfile for specific user in /etc/security/limits.d/{{ user }}
copy:
dest: "/etc/security/limits.d/{{ item }}"
content: |
"{{ item }}" - nofile 128000
"{{ item }}" - nproc 65536
with_items:
- kube
register: output
- debug:
msg: "{{item.item}}"
loop: "{{output.results}}"
loop_control:
label: "{{item.item}}"
- name: set permission and change owner of /etc/security/limits.d/{{ user }}
file:
path: "/etc/security/limits.d/{{ item.item }}"
owner: root
group: root
mode: '0644'
with_items:
"{{output.results}}"
Make sure to run this with become: yes. we are storing the output in the "output variable". if there are multiple values in with items. it will store the multiple items output in the output variable(output.results). the output.results was giving the complex data structure output. so i have use loop_control(lable) to limit the output. the item.item give the output kube.

Related

Resursively sync files and links to multiple users, preserving permissions, but not owner and group

I have a directory with several tools and text files that I would like to copy to multiple users homedirs on given host. There are couple of caveats:
I don't want to list the files in playbook
Some files are executable, some are not
I want the files to be owned by their respective users
I want the symlinks to be copied as-is
When pushing to ansible_user home dir, there's no issue as ansible.posix.synchronize does the job very well with archive=true and owner/group set to false:
- name: Sync testing_files repo to ansible_user
ansible.posix.synchronize:
src: ~/testing_files/
dest: ~/testing_files/
mode: push
archive: true
delete: true
owner: false
group: false
register: rsync_output
The links are properly handled as well (they are rsynced as symlinks).
However, the problem is with populating the same for other users. I tried the following approach:
- name: Sync testing_files repo to extra_users
ansible.builtin.copy:
src: ~/testing_files/
dest: ~{{ item }}/testing_files/
remote_src: true
mode: preserve
owner: "{{ item }}"
follow: true # I tried with `false` as well
with_items:
- "{{ extra_users if extra_users is not none else [] }}"
The file permissions are correct, the owner as well, however:
the group of file remains the same as source file
the symlinks are ignored
How can I make it work? For the group issue the only solution I came up is to have another task that will run stat to check group and save it for future use, e.g. like this:
- name: Get group of homedir
ansible.builtin.stat:
path: "~{{ item }}"
register: homedir
with_items:
- "{{ extra_users_or_empty }}"
- name: Sync testing_files repo to extra_users
ansible.builtin.copy:
src: ~/testing_files/
dest: "~{{ item }}/testing_files/"
remote_src: true
mode: preserve
owner: "{{ item }}"
group: "{{ homedir.results | selectattr('item', 'eq', item) | map(attribute='stat.gr_name') | first }}"
follow: true
with_items:
- "{{ extra_users_or_empty }}"
(NOTE: extra_users_or_empty: "{{ extra_users if extra_users is not none else [] }}")
However that feels like something that should be achieved in more elegant way. And for symlinks - I have no idea why the ansible.builtin.copy ignores them.
Any ideas?
Huh, ok, seems like when we're using remote_src, we should set local_follow, instead of follow. The following solution handles symlinks properly:
- name: Get group of homedir
ansible.builtin.stat:
path: "~{{ item }}"
register: homedir
with_items:
- "{{ extra_users_or_empty }}"
- name: Sync testing_files repo to extra_users
ansible.builtin.copy:
src: ~/testing_files/
dest: "~{{ item }}/testing_files/"
remote_src: true
mode: preserve
owner: "{{ item }}"
group: "{{ homedir.results | selectattr('item', 'eq', item) | map(attribute='stat.gr_name') | first }}"
local_follow: false
with_items:
- "{{ extra_users_or_empty }}"

Use Ansible to ensure a file exists, ignoring any extra lines

I'm trying to update the sssd.conf file on about 200 servers with a standardized configuration file, however, there is one possible exception to the standard. Most servers will have a config that looks like this:
[domain/domainname.local]
id_provider = ad
access_provider = simple
simple_allow_groups = unixsystemsadmins, datacenteradmins, sysengineeringadmins, webgroup
default_shell = /bin/bash
fallback_homedir = /export/home/%u
debug_level = 0
ldap_id_mapping = false
case_sensitive = false
cache_credentials = true
dyndns_update = true
dyndns_refresh_interval = 43200
dyndns_update_ptr = true
dyndns_ttl = 3600
ad_use_ldaps = True
[sssd]
services = nss, pam
config_file_version = 2
domains = domainname.local
[nss]
[pam]
However, on some servers, there's an additional line after simple_allow_groups called simple_allow_users, and each server that has this line has it configured for specific users to be allowed to connect without being a member of an LDAP group.
My objective is to replace the sssd.conf file on all servers, but not to remove this simple_allow_users line, if it exists. I looked into lineinfile and blockinfile, but neither of these seems to really handle this exception. I'm thinking I'm going to have to check the file for the existance of the line, store it to a variable, push the new file, and then add the line back, using the variable afterwards, but I'm not entirely sure if this is the best way to handle it. Any suggestions on the best way to accomplish what I'm looking to do?
Thanks!
I would do the following
See if the simple_allow_users exists in the current sssd.conf file
Change your model configuration to add the current value of the line simple_allow_users is exists
overwrite the sssd.conf file with the new content
You can use jinja2 conditional to achieve step 2 https://jinja2docs.readthedocs.io/
I beleive the above tasks will solve what you need, just remember to test on a simngle host and backup the original file just for good measure ;-)
- shell:
grep 'simple_allow_users' {{ sssd_conf_path }}
vars:
sssd_conf_path: /etc/sssd.conf
register: grep_result
- set_fact:
configuration_template: |
[domain/domainname.local]
id_provider = ad
access_provider = simple
simple_allow_groups = unixsystemsadmins, datacenteradmins, sysengineeringadmins, webgroup
{% if 'simple_allow_users' in grep_result.stdout %}
{{ grep_result.stdout.rstrip() }}
{% endif %}
default_shell = /bin/bash
..... Rest of your config file
- copy:
content: "{{ configuration_template }}"
dest: "{{ sssd_conf_path }}"
vars:
sssd_conf_path: /etc/sssd.conf
I used Zeitounator's tip, along with this question Only check whether a line present in a file (ansible)
This is what I came up with:
*as it turns out, the simple_allow_groups are being changed after the systems are deployed (thanks for telling the admins about that, you guys... /snark for the people messing with my config files)
---
- name: Get Remote SSSD Config
become: true
slurp:
src: /etc/sssd/sssd.conf
register: slurpsssd
- name: Set simple_allow_users if exists
set_fact:
simpleallowusers: "{{ linetomatch }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: linetomatch
vars:
- decode_content: "{{ slurpsssd['content'] | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"simple_allow_users" in linetomatch'
- name: Set simple_allow_groups
set_fact:
simpleallowgroups: "{{ linetomatch }}"
loop: "{{ file_lines }}"
loop_control:
loop_var: linetomatch
vars:
- decode_content: "{{ slurpsssd['content'] | b64decode }}"
- file_lines: "{{ decode_content.split('\n') }}"
when: '"simple_allow_groups" in linetomatch'
- name: Install SSSD Config
copy:
src: etc/sssd/sssd.conf
dest: /etc/sssd/sssd.conf
owner: root
group: root
mode: 0600
backup: yes
become: true
- name: Add simple_allow_users back to file if it existed
lineinfile:
path: /etc/sssd/sssd.conf
line: "{{ simpleallowusers }}"
insertafter: "^simple_allow_groups"
when: simpleallowusers is defined
become: true
- name: Replace simple allow groups with existing values
lineinfile:
path: /etc/sssd/sssd.conf
line: "{{ simpleallowgroups }}"
regexp: "^simple_allow_groups"
backrefs: true
when: simpleallowgroups is defined
become: true

Ansible Create SubFolders Matching Pattern

I have an ansible playbook, that creates directories by passed an array of directories, owner, and permissions. Our admins are worried, someone will create directories under our O/S Volumes and cause issues with the system. Since we only have a few folders that require root, I'm researching how to whitelist specific folders that are passed in for root only. Other directories and use our internal user to directories that don't require root.
This is what I've come up with, but I have concern with /vs_volue/etc instead of /etc being passed. I can't find a starts with /etc for example. Is there a better way?
---
- name: Create Directories
hosts: target_hosts
vars:
dir_list: '{{ dir_list }}'
permissions: {{ permissions }}
linux_user: 'webuser'
whitelist_dir:
- "/etc"
- "/usr"
tasks:
- name: User to root when creating folders in /etc or /usr
set_fact:
linux_user: "root"
when: dir_list|string|regex_search('{{ item }}')
with_items:
- "{{ whitelist_dir }}"
- name: Create Directories as WebUser by Directory Array Lists by Line Feed
file:
path: "{{ item }}"
mode: "{{ permissions }}"
recurse: yes
state: directory
become: true
become_user: "{{ linux_user }}"
with_items: "{{ dir_list.split('\n') }}"
when: dir_list | search('\n')
Try this.
main.yml
- hosts: target_hosts
vars:
default_linux_user: "webuser"
permissions: "{{ permissions | default(0664) }}"
whitelist_dir:
- "^/etc/.*"
- "^/usr/.*"
tasks:
- include_tasks: create_dir.yml
loop: "{{ dir_list.split('\n') }}"
loop_control:
loop_var: dir
create_dir.yml
- block:
- set_fact:
linux_user: "{{ 'root' if dir is regex(item) else default_linux_user }}"
when: linux_user is undefined and (not linux_user == 'root')
loop: "{{ whitelist_dir }}"
- debug:
msg: "For {{ dir }} - {{ linux_user }} will be set as owner"
- file:
path: "{{ dir }}"
state: directory
mode: "{{ permissions }}"
owner: "{{ linux_user | default(default_linux_user) }}
recurse: yes
become: true
become_user: root
become_method: sudo
always:
- set_fact:
linux_user: default_linux_user

Pass dictionary to jinja2 template from task loop

I pass dictionary from play to task. I use loop to call another task from separate yml file again passing the dictionary. From there I call Jinja2 template and pass the dictionary again. I cannot access the dictionary values from Jinja2.
I tried to pass the dictionary to template with_items and with_dict. Still the same problem.
play:
- role: example
vars:
brands:
brand_1:
name: "brand1"
brand_2:
name: "brand2"
brand_3:
name: "brand_3"
task in role with loop:
- name: Loop through configuration files
include_tasks: generate_config_files.yml
loop: "{{ lookup('dict', brands) }}"
loop_control:
loop_var: outer_item
generate_config_files.yml
- name: Generate the configuration files
template:
src: "consumer.properties.j2"
dest: "{{ kafka_location }}/{{ item.key }}/consumer.properties"
owner: "{{ kafka_user }}"
group: "{{ kafka_group }}"
mode: 0644
with_dict: "{{ outer_item }}"
consumer.properties.j2
{% for item in outer_item %}
Name: "{{ item.name }}"
{% endfor %}
I expect to access the dictionary value in template and generate the same file with different values based on number of brands in dictionary. So if there are 3 brands I expect to generate 3 files with different Name: inside.
Unfortunately I am getting:
"msg": "AnsibleUndefinedVariable: 'str object' has no attribute 'name'"
Any ideas?
1) Indentation of vars: is wrong.
2) The single loop does the job.
3) Iteration in the template is not necessary.
4) Numeric mode must be quoted mode: '0644'.
The playbook below
- hosts: localhost
roles:
- role: example
vars:
kafka_user: admin
kafka_group: admin
kafka_location: /scratch
brands:
brand_1:
name: "brand1"
brand_2:
name: "brand2"
brand_3:
name: "brand_3"
with tasks
$ cat roles/example/tasks/main.yml
- include_tasks: generate_config_files.yml
, with the included task
$ cat roles/example/tasks/generate_config_files.yml
- name: Generate the configuration files
template:
src: "consumer.properties.j2"
dest: "{{ kafka_location }}/{{ item.key }}/consumer.properties"
owner: "{{ kafka_user }}"
group: "{{ kafka_group }}"
mode: '0644'
loop: "{{ brands|dict2items }}"
, and with the template
$ cat roles/example/templates/consumer.properties.j2
Name: "{{ item.value.name }}"
gives
$ tree /scratch/brand_*
/scratch/brand_1
└── consumer.properties
/scratch/brand_2
└── consumer.properties
/scratch/brand_3
└── consumer.properties
$ cat /scratch/brand_*/consumer.properties
Name: "brand1"
Name: "brand2"
Name: "brand_3"
Is this what you're looking for?

Can't copy files using loop

I'm trying to copy many files using ansible.
this is my playbook :
- name: Copy the scenario test
copy:
src: files/{{ scenario_name }}
dest: /home/{{ user }}/scenario_creation
mode: '0644'
run_once: true
loop: "{{ scenario_name }}"
tags:
- user
- scenario
and this is my roles/scenario_test/defaults/main.yml
scenario_name: ['topup-scenario.json', 'test.json']
when I execute my playbook it says:
"msg": "Could not find or access 'files/[u'topup-scenario.json', u'test.json']'\nSearched in:\n\t/home/path/ansible/plays/files/[u'topup-scenario.json', u'test.json']\n\t/home/path/ansible/plays/files/[u'topup-scenario.json', u'test.json'] on the Ansible Controller.\nIf you are using a module and expect the file to exist on the remote, see the remote_src option"
}
any help ?
Change:
src: files/
to
src: ./files/
You need to change your code to this:
- name: Copy the scenario test
copy:
src: files/{{ item }}
dest: /home/{{ user }}/scenario_creation
mode: '0644'
run_once: true
loop: "{{ scenario_name }}"
tags:
- user
- scenario
The loop iterates the list to the term 'item', unless you redefine it with the loop_var option. So when you call scenario_name in your src line, you are actually calling the entire list, not an iteration of it.

Resources