Ansible jinja 2 template value should not changed - ansible

I have written jinja2 template in ansible. What i am trying to achieve is that if the service_name is not mentioned and if service_name already exists on the remote machine, ansible should not change the service_name with default name mentioned in the template. However, when the service_name is not defined, ansible replaces service name with "abc" on remote machine even service_name exists. Any help would be appreciated.
active={{ active_status}}
instrument={{ instrument_status }}
{% if service_name is defined %}
service_name={{ service_name }}
{% else %}
service_name=abc
{% endif %}
Thanks

Following my above comment, here is a possible example implementation to meet your requirements. test_template.j2 is the exact copy of your current template. You can pass the service name as an extra variable to test (-e service_name=my_service)
Basically, if service_name is not defined, we:
Check if the remote file already exists and slurp its content into a var
Look for the relevant line in the file. Note: the regex_replace('None', '') is here to make sure we get an empty string if previous search/matches did not return anything.
Set the service name only if something relevant was found in the prior tasks
Once this check/setting is done correctly, you simply have to copy your template, what ever the case is.
---
- name: Conditional writing of template
hosts: localhost
gather_facts: false
vars:
my_dest: /tmp/test_file.txt
active_status: some active value
instrument_status: some instrument value
tasks:
- name: Try to read service name from existing file when it is not defined
when: service_name is not defined
block:
- name: Check if file exists
stat:
path: "{{ my_dest }}"
register: my_file
- name: Try to read target file if exists
slurp:
src: "{{ my_dest }}"
when: my_file.stat.exists
register: my_file_slurp
- name: Look for service name if there
set_fact:
looked_service: >-
{{
my_file_slurp.content
| b64decode
| regex_search('^service_name=.*$', multiline=true)
| regex_replace('^service_name=(.*)$', '\1')
| regex_replace('None', '')
}}
when: my_file.stat.exists
- name: Update service name if found
set_fact:
service_name: "{{ looked_service }}"
when: looked_service | default('') | length > 0
- name: Copy template file to destination
template:
src: test_template.j2
dest: "{{ my_dest }}"

Related

Ansible run shell module on multiple host's and redirect output to 1 file

I need to run shell module on all hosts group and copy the register variable to a file on any server.
NOTE : I don't want to copy the results in my local i need it on server
- name: date.
shell: cat /ngs/app/user/test
register: date_res
changed_when: false
- debug:
msg: "{{ ansible_play_hosts | map('extract', hostvars, 'date_res') | map(attribute='stdout') | list }}"
run_once: yes
- name: copy bulk output
copy:
content: "{{ allhost_out.stdout }}"
dest: "/ngs/app/{{ app_user }}/test"
Jinja2 loop can be helpful for your case.
Tested on ansible [core 2.13.3]
- name: date.
shell: cat /ngs/app/user/test
register: date_res
changed_when: false
- name: copy bulk output
copy:
content: |
{% for host in vars['play_hosts'] %}
{{ host }} {{ hostvars[host].date_res.stdout }}
{% endfor %}
dest: "/ngs/app/{{ app_user }}/bldall"
when: inventory_hostname == host-001.example.com
On host-001.example.com placed a file contains: host1 file content.
On host-002.example.com placed a file contains: host2 file content.
Output on host specified in the copy task:
host-001.example.com host1 file content
host-002.example.com host2 file content

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

Missing variable in jinja2, no output with Ansible

I have an Ansible playbook that gathers facts from Cisco switches.
---
- hosts: switches
gather_facts: False
connection: network_cli
vars:
backup_root: ./configs
cli:
host: "{{ inventory_hostname }}"
tasks:
- name: ensure device folder is created
file:
path: "{{ backup_root }}/{{ inventory_hostname }}"
state: directory
- name: Gather all facts
cisco.ios.ios_facts:
gather_subset: all
- name: Serial Number
debug: var=ansible_net_serialnum
- name: Model
debug: var=ansible_net_model
- name: Hostname
debug: var=ansible_net_hostname
- name: Version
debug: var=ansible_net_version
- name: CDP
debug: var=ansible_net_neighbors
- name: Config file
debug: var=ansible_net_config
- name: Stack SW Model Numbs
debug: var=ansible_net_stacked_models
- name: Stack SW Model Numbs
debug: var=ansible_net_stacked_serialnums
- name: Get VLAN Info
cisco.ios.ios_command:
commands: show vlan brief
register: show_vlan
- name: get timestamp
command: date +%Y%m%d
register: timestamp
- name: Generate configuration files
template:
src=roles/discovery/templates/ios_switches.j2
dest="{{ backup_root }}/{{ inventory_hostname }}/{{ inventory_hostname }}.txt" `
Here is the jinja file.
Hostname: {{ ansible_net_hostname }}
Model: {{ansible_net_model}}
Serial Number: {{ansible_net_serialnum}}
IOS Version: {{ansible_net_version}}
IOS Image: {{ansible_net_image}}
Switch Stack Models:
{{ansible_net_stacked_models | to_nice_yaml(indent=2)}}
Switch Stack Serials:
{{ansible_net_stacked_serialnums | to_nice_yaml(indent=2)}}
CDP Neighbors:
{{ansible_net_neighbors | to_nice_yaml(indent=2)}}
Configuration:
{{ansible_net_config}}
VLAN:
{{show_vlan.stdout[0] | to_nice_yaml(indent=2)}}
This all works fine until it hits a switch that cannot stack (e.g. chassis or VSS). When I run the playbook, I get the following-
msg: 'AnsibleUndefinedVariable: ''ansible_net_stacked_models'' is undefined
I've tried using if in Jinja2 like the following
...
Switch Stack Models:
{% if ansible_net_stacked_models is not defined %}
NOT A STACKABLE SWITCH
{% else %}
{{ansible_net_stacked_models | to_nice_yaml(indent=2)}}
{% endif %}
however it fails in the Jinja rendering and does not produce any output.
Is there a way to ignore missing variables in jinja or is there a better way to do this?
you can set a default value to your variable if is not defined
{{ ansible_net_stacked_models|default("NOT A STACKABLE SWITCH", true) | to_nice_yaml(indent=2) }}
Come to find out the error was stopping Ansible from even calling the jinja file. I dug around some more and found that I could set the variable to a default value in the inventory.
[switch:vars]
ansible_net_stacked_models='NOT A STACKABLE SWITCH'
When running the playbook if the device has valid info it will overwrite the default variable we defined in inventory. If the device doesn't have valid info, Ansible will simply pass the default variable we set in inventory, over to jinja2.

Replace host variables in Jijnja template file in ansible

I need to replace a file with the variables defined in the host file.
Following is the host variables defined in "host_vars/abc.1234.com"
env: acc
abcserverName:
- name: abc1
- name: abc2
The playbook file has the following contents
- hosts: "abc.1234.com"
become: yes
tasks:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
- name: start abcserver
command: /etc/init.d/control-{{ env }}-{{ item.name }} start
with_items:
- "{{ abcserverName }}"
This will copy 2 files in init.d, which is the following:
/etc/init.d/control-acc-abc1
/etc/init.d/control-acc-abc2
Requirement:
Inside each of the above control files, I also need to get the correct "abcserverName"
For example in "/etc/init.d/control-acc-abc1", I want
SERVER_NAME=abc1
and in For example in "/etc/init.d/control-acc-abc2"
SERVER_NAME=abc2
I don't have much knowledge about jinja templates and google shows me complex examples. Any help to achieve this is appreciated.
Take this jinja2 code for example:
SERVER_NAME={{ server_name }}
server_name is not actually defined, and to use it you will need to pass it as a variable to your template.
Using your task:
- name: deploy abc control file
template:
src: abc-control.j2
dest: /etc/init.d/{{ env }}-{{ item.name }}
with_items:
- "{{ abcservername }}"
vars:
server_name: {{ item.name }}
Now "control-acc-abc1" will have server_name set to abc1 and "control-acc-abc2" will have server_name set to abc2

Ansible recursive checks in playbooks

We need to go through this structure
Zone spec
https://gist.github.com/git001/9230f041aaa34d22ec82eb17d444550c
I was able to run the following snipplet but now I'm stucked at the error checking.
playbook
--
- hosts: all
gather_facts: no
vars_files:
- "../doc/application-zone-spec.yml"
roles:
- { role: ingress_add, customers: "{{ application_zone_spec }}" }
role
- name: check if router exists
shell: "oc get dc -n default {{ customers.zone_name }}-{{ item.type }}"
with_items: "{{ customers.ingress }}"
ignore_errors: True
register: check_router
- name: Print ingress hostnames
debug: var=check_router
- name: create new router
shell: "echo 'I will create a router'"
with_items: "{{ customers.ingress }}"
when: check_router.rc == 1
Output of a ansible run
https://gist.github.com/git001/dab97d7d12a53edfcf2a69647ad543b7
The problem is that I need to go through the ingress items and I need to map the error of the differnt types from the "check_router" register.
It would be nice to make something like.
Pseudo code.
Iterate through the "customers.ingress"
check in "check_router" if the rc is ! 0
execute command.
We use.
ansible-playbook --version
ansible-playbook 2.1.0.0
config file = /etc/ansible/ansible.cfg
configured module search path = Default w/o overrides
You can replace the second loop with:
- name: create new router
shell: "echo 'I will create a router with type {{ item.item }}'"
with_items: "{{ check_router.results }}"
when: item.rc == 1
This will iterate over every step of check_route loop and you can access original items via item.item.

Resources