How to get the return code in saltstack state - jinja2

Do you know how I can parse the data from stdout and searching within it?
check_satellite_registration:
cmd.run:
- name: subscription-manager list
For example, see the return data below. How can I evaluate the Status in stdout?
retcode:
0
stderr:
stdout:
Status: Subscribed
If the above status is "Subscribed" then continue the execution of the states, otherwise redo the installation:
deploy the http.conf file:
file.managed:
- name: /etc/http/conf/http.conf
- source: salt://apache/http.conf

There are a couple of options. They all involve not using a state for the check, because it doesn't do anything stateful.
deploy the http.conf file:
file.managed:
- name: /etc/http/conf/http.conf
- source: salt://apache/http.conf
- onlyif:
- subscription-manager list | grep 'Status: Subscribed'
do something else:
cmd.run:
- unless:
- subscription-manager list | grep 'Status: Subscribed'
{% set status = salt["cmd.run"]("subscription-manager list") %}
{% if "Status: Subscribed" in status %}
deploy the http.conf file:
file.managed:
- name: /etc/http/conf/http.conf
- source: salt://apache/http.conf
{% else %}
do something else:
cmd.run: []
{% endif %}
Though if your "something else" action is supposed to result in the status becoming subscribed, then you should do it like this instead:
register satellite:
cmd.run:
- name: my-register-command
- unless:
- subscription-manager list | grep 'Status: Subscribed'
deploy the http.conf file:
file.managed:
- name: /etc/http/conf/http.conf
- source: salt://apache/http.conf
- require:
- register satellite

Related

Replace text using saltstack

I will need your assistance on the following:
I would like to replace an IP address within a file, regex is matching, but the output is totally wrong.
test.sls
wsc_start_server:
file.managed:
- name: /tmp/modify.txt
- source: salt://modify.txt
{% set ip = grains['fqdn_ip4'] %}
wsc_jbos_replace_hostname:
file.replace:
- name: /tmp/modify.txt
- pattern: '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
- repl: {{ ip }}
- backup: '.bak'
- show_changes: True
modify.txt
address=y.y.y.y
address.private=y.y.y.y
salt 'workstation' state.apply test.sls
output:
address=['x.x.x.x']
address.private=['x.x.x.x']
When I am running the state why I am getting the ip address in ['x.x.x.x'] format instead of address=x.x.x.x ????
Note: y.y.y.y & x.x.x.x are IP Addresses
The issue is that the fqdn represent a list.
to fix the issue I change the following:
{% set ip = grains['fqdn_ip4'] %}
wsc_jbos_replace_hostname:
file.replace:
- name: /tmp/modify.txt
- pattern: '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
- repl: {{ ip }}
- backup: '.bak'
- show_changes: True
to the correct one:
{% set ip = grains['fqdn_ip4'][0] %}
wsc_jbos_replace_hostname:
file.replace:
- name: /tmp/modify.txt
- pattern: '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
- repl: {{ ip }}
- backup: '.bak'
- show_changes: True
it worked. :) :)

Extracting values from YAML into Jinja template for Ansible playbook

I have a YAML file with content as like below:
cat ../../ansible/playbooks/vars/patching-config.yml
---
patching_tag_name: "Patching"
my_windows_patching:
- {
OS: "WINDOWS",
tag_value: "myProdA",
frequency: "Month", #patching frequency. OneTime|Day|Hour|Week|Month|Minute
interval: 1, #interval of the schedule.
rebootSetting: "never", #ifRequired|never|always
PatchGroup: testA,
startDate: "2020-01-16T23:59:59Z",
expiryDate: "2020-02-16T23:59:59Z",
duration: "PT2H0M",
timeZone: "Australia/Sydney",
updateClassifications: "Critical,Important,Moderate"
}
I want to extract the values of updateClassifications from above YML file in Jinja Template file MaintenanceWindow.yml.j2
Resources:
WindowsNonProdBaseline:
Type: AWS::SSM::PatchBaseline
Properties:
Name: Windows-Non-Prod-Baseline
Description: Baseline containing all updates approved for Windows instances
OperatingSystem: {{ item.OS }}
PatchGroups:
- {{ item.PatchGroup }}
ApprovalRules:
PatchRules:
- PatchFilterGroup:
PatchFilters:
- Values:
# - Critical
# - Important
# - Moderate
{% for item in item.updateClassifications %}
- {{ item }}
{% endfor %}
I'm trying with the code described above, below one more time snippet:
{% for item in item.updateClassifications %}
- {{ item }}
{% endfor %}
I'm calling patching-config.yml in my tasks/main.yml as below
- include_vars: "{{playbook_dir}}/vars/patching-config.yml"
ignore_errors: yes
- name: create a cloudformation stack
cloudformation:
stack_name: "New-Ansible-cloudformation"
state: "present"
disable_rollback: true
template_body: "{{ lookup('template', '../../cloudformation/patching/MaintenanceWindow.yml.j2') }}"
with_items: "{{ telstra_windows_patching }}"
Finally, invoking role as below
cat ansible/playbooks/patching.yml
---
- hosts: localhost
roles:
- patching-cf-ssm
Unfortunately, it is not working.
Any lead shall be greatly appreciated.
Couple of things:
Your task is using telstra_windows_patching in with_items where as your variable file has variable name as my_windows_patching.
Assuming you are using the same name say my_windows_patching in your task and var file, if you are trying to save json object in yaml variable my_windows_patching you don't need - before curly braces. You can define something like this
my_windows_patching:
{
OS: "WINDOWS",
tag_value: "myProdA",
frequency: "Month", #patching frequency. OneTime|Day|Hour|Week|Month|Minute
interval: 1, #interval of the schedule.
rebootSetting: "never", #ifRequired|never|always
PatchGroup: testA,
startDate: "2020-01-16T23:59:59Z",
expiryDate: "2020-02-16T23:59:59Z",
duration: "PT2H0M",
timeZone: "Australia/Sydney",
updateClassifications: "Critical,Important,Moderate"
}
If you want to use elements inside my_windows_patching object with dot notation directly you could change the variable from object to a list something like,
my_windows_patching:
- OS: "WINDOWS"
tag_value: "myProdA"

Rendering SLS: mapping values are not allowed here

I try to place / remove a drop in config file based on a PILLAR variable.
This was put in highstate/init.sls on my salt master.
I cannot see any syntax error or spacing issues here.
What am i doing wrong here?
Here my SLS file
(% if pillar['highstateenabled'] == 'true' %)
enable_highstate:
file.managed:
- name: /etc/salt/minion.d/highstate.conf
- source: salt://common/salt-minion/files/minion.d/highstate.conf
- template: jinja
(% else %)
disable_highstate:
file.absent:
- name: /etc/salt/minion.d/highstate.conf
(% endif %)
And the output during salt-call:
---
local:
Data failed to compile:
----------
Rendering SLS 'LAB:common.salt-minion' failed: mapping values are not allowed here; line 4
---
(% if pillar['highstateenabled'] == 'true' %)
enable_highstate: <======================
file.managed:
- name: /etc/salt/minion.d/highstate.conf
- source: salt://common/salt-minion/files/minion.d/highstate.conf
- template: jinja
[...]
---
finally i found the solution myself:
The code above is using the wrong brackets.
Instead using "()" it should be curly brackets "{}" and the code is beeing parsed correctly.
The SLS Rendering error does not point to a syntax error here...
{% if pillar['highstate'] == 'enabled' %}
output:
cmd.run:
- name: 'echo "highstate_enabled" '
enable_highstate:
file.managed:
- name: /etc/salt/minion.d/highstate.conf
- source: salt://common/salt-minion/files/minion.d/highstate.conf
- template: jinja
{% else %}
disable_highstate:
file.absent:
- name: /etc/salt/minion.d/highstate.conf
{% endif %}
"seems sometimes you don't see the forest because of too many tree's"

Jinja2 filter list using string contains test

I'm trying to filter a list in ansible in Jinja2 when the elements contain a string, but the Jinja documentation doesn't seem clear enough for me to figure it out.
This is what I have so far:
- name: run script
command: /usr/tmp/run_script.py
register: script_results
- name: display run info
debug:
var: "{{script_results.stdout_lines | select(\"'running script' in script_results.stdout_lines\") }}"
But all I get is the error:
"<generator object _select_or_reject at 0x13851e0>": "VARIABLE IS NOT DEFINED!"
So for example, if stdout_lines contains ["apples","running script one","oranges","running script two"], I want to print
running script one
running script two
They have documentation for select and documentation for built-in-tests, but they don't display the "in" test, and I don't know how they work in the context of this ansible variable.
I tried solving it like this:
- name: display run info
debug:
var: item
with_items: "{{script_results.stdout_lines}}"
when: "'running script' in item"
But that displays "skipping" for every line that doesn't pass the test ... kinda defeating the purpose!
The select filter would take another filter. Like in the docs odd, which will return only the odd elements of the list. The filter you would like to combine select with is equalto.
Now here's the thing. Ansible bundles a very old version of Jinja2, which simply does not contain the equalto filter. Yes, that renders it useless unless you want to filter odd elements. (Which nobody ever in history wanted to...)
Furthermore I was yet unable to make custom filter plugins work in Ansible 2. So you're pretty much forced to hack something ugly together.
helloV already showed one option. Here is another idea:
- name: run script
shell: /usr/tmp/run_script.py | grep "running script"
register: script_results
Update:
I recently discovered you can use match (not a standard Jinja2 filter but added by Ansible) together with select. Thats a good replacement for the eualto filter plus you can use regular expressions. This should work:
{{ script_results.stdout_lines | select("match", ".*running script.*") }}
I understand there may be more than one way to do this. Will this work for you?
- debug: var={{item}}
when: item.find('running script') > -1
with_items: script_results.stdout_lines
I know this is an old thread but I too was looking for an answer to this and I was able to use the if in method. I think the other issue you were having was how to display the resulting list. You can do a jinja loop right in the ansible modules, debug for example. Of course since Ansible is horrible with presenting data using debug, you could also utilize the blockinfile module with the pipe (|). Hopefully this helps others and gives yet another option.
- debug:
msg: |
{% for item in (script_results.stdout_lines) %}
{% if 'running script' in item %}
{{ item }}
{% endif %}
{% endfor %}
tags: debug
or to add the filtered data to a file:
- name: Update the scriptStatus file.
delegate_to: localhost
run_once: TRUE
blockinfile:
path: '/tmp/scriptStatus.txt'
block: |
{% for item in (script_results.stdout_lines) %}
{% if 'running script' in item %}
{{ item }}
{% endif %}
{% endfor %}
tags: chkScripts
I ended up writing a python script to do it, because I couldn't get ansible or ancient-jinja2 to make the cut.
Ansible tasks:
- name: gather run info
command: "{{role_path}}/files/print_results.py {{script_results.stdout_lines}}"
register: script_print_results
delegate_to: 127.0.0.1
run_once: true
- name: display run info
debug:
var: script_print_results.stdout_lines
delegate_to: 127.0.0.1
run_once: true
Python script:
for result_line in sys.argv[1:]:
if "running script:" in result_line:
print result_line[1:-1]
You can build a new list with set_fact and print the elements of a new list.
- hosts: localhost
gather_facts: false
vars:
script_stdout_lines:
- apples
- running script one
- oranges
- running script two
tasks:
- set_fact:
new_list: "{{ new_list | default([]) + [item] }}"
with_items: "{{ script_stdout_lines }}"
when: '"running script" in item'
- debug: var=new_list
Result:
TASK [set_fact] *********************************************************************************************************************
skipping: [localhost] => (item=apples)
ok: [localhost] => (item=running script one)
skipping: [localhost] => (item=oranges)
ok: [localhost] => (item=running script two)
TASK [debug] ************************************************************************************************************************
ok: [localhost] => {
"new_list": [
"running script one",
"running script two"
]
}
It prints skipping during set_fact operation but at the end it provides a new list with the only matching items.

Salt: Can I use an argument from the command line as a jinja variable?

Given a file called package-list, I want to do something like:
salt state.sls install-packages list_to_install=package-list
...and then in the sls:
packages:
pkg.installed:
- names:
{% include list_to_install %}
You can do this using Pillar:
packages:
pkg:
- installed
- pkgs: {{ salt['pillar.get']('packages') }}
Then pass the pillar argument containing valid YAML:
salt '*' state.sls package-list pillar='{packages: [foo, bar, baz]}'