Ansible INI lookup plugin gives ['<element>'], instead of <element> - regex

I have a users.ini file having below content:
[integration]
# My integration information
user=gertrude
pass=anotherpassword
I am trying to fetch the value in my below yml file using lookup plugin for INI:
- hosts: "{{vnf_ip}}"
connection: local
tasks:
debug: msg="User in integration is {{ lookup('ini', 'user section=integration file=users.ini') }}"
But I am getting output as
TASK [debug] ***********************************************************************************************************************************
ok: [10.10.10.10] => {
"msg": "User in integration is ['gertrude']"
}
Instead of ['gertrude'] it should simply be gertrude.
How to get gertrude simply????

What Ansible version do you use? On modern 2.3.2 it works as expected and returns just gertrude.
If you can't upgrade, you can use first filter to get an element from your resulting list:
{{ lookup('ini', 'user section=integration file=users.ini') | first }}

Related

How to Use a Loop to Render Multiple Templates with multiple variables

I have a task to install monitoring agent in multiple hosts.
we are using three templates(yml files) during this installations. these template contain multiple tags. such as app name, teamdl, server IP, datacenter and so on...
for bulk installation we are using csv file which contains value for each tag.
Ask is to use each item in the csv file and use them as variable for all three templates.
I am able to read these items, but not able to use them. please support.
main.yml
- name: copy the csv file
copy:
src: "/u00/ansible/Playbooks/test.csv". # present in ansible controller
dest: "/u00/app/monitor/test.csv" #target server
become: yes
become_user: root
vars:
contents: "{{ lookup('file', '/u00/app/monitor/test.csv').split('\n') }}"
- name: Update config yml files
template: src={{item.src}} dest={{item.dest}}
loop:
- { src: '/u00/ansible/Playbooks/files/infra.yml_template', dest: '/u00/app/monitor/infra.yml' }
infra.yml_template -
custom_attributes:
application : {{ item.Application }}
env : {{ item.env }}
datacenter : {{ item.Datacenter }}
log:
file: /u00/app/monitor/infra.log
format: text
level: smart
forward: false
stdout: false
smart_level_entry_limit: 500
rotate:
max_size_mb: 100
max_files: 5
compression_enabled: true
file_pattern: rotated.YYYY-MM-DD_hh-mm-ss.log
error -
TASK [infra-integration : copy the csv file] ********************************
[0;32mok: [testserver1][0m
[0;32mok: [Testserver2][0m
TASK [Infra-integration : Update config yml files] **************************
[0;31mAn exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.errors.AnsibleUndefinedVariable: 'dict object' has no attribute 'Application'[0m
[0;31mfailed: [testserver1] (item={'src': '/u00/ansible/Playbooks/files/infra.yml_template', 'dest': '/u00/app/monitor/infra.yml'}) => {"ansible_loop_var": "item", "changed": false, "item": {"dest": "/u00/app/monitor/infra.yml", "src": "/u00/ansible/Playbooks/files/infra.yml_template"}, "msg": "AnsibleUndefinedVariable: 'dict object' has no attribute 'Application'"}[0m
Expectation is to read csv file and use variables in three different template located at three different location.

Ansible - How to do integer comparison in a conditional?

I am trying to do a simple integer comparison in a conditional but something is up, and it is not evaluating properly.
Code within playbook:
- name: Check the version of the current sql database on both dispatchers and save that value.
command: /usr/bin/mysql -V
changed_when: false
register: sqlversion
- name: Print to the screen the current sql database version.
debug:
var: "{{ sqlversion.stdout.split()[4] | regex_search('[0-9]+\\.[0-9]+') | replace(\".\",'') }}"
register: w
- name: Show the value of the variable.
debug:
var: w
- name: Test result
command: w
when: ( w | int < 55 )
The output of the command module (ultimately to get the 5.5 part of the 5.5.43 number:
mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2
My actual run in which the Test result task "fails" as in it runs when it should not, because of the evaluation problem:
TASK [Check the version of the current sql database on both dispatchers and save that value.] ***********
ok: [server2]
ok: [server1]
TASK [Print to the screen the current sql database version.] ********************************************
ok: [server1] => {
"55": "55"
}
ok: [server2] => {
"55": "55"
}
TASK [Show the value of the variable.] ******************************************************************
ok: [server1] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
ok: [server2] => {
"w": {
"55": "55",
"changed": false,
"failed": false
}
}
TASK [Test result] **************************************************************************************
changed: [server1]
changed: [server2]
This is not right or expected, clearly the last task shows "changed" in that it ran, and evaluated the condition as true when it shouldn't be. Instead with how I coded the conditional, it should be skipped instead! Additionally, if I take away the " | int" then it always skips no matter what the number(s) are.
What's the issue here? There's got to be a way to make this work.
Simplify the parsing
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
gives
w: 5.5.43
Use the test version. See Comparing versions. For example,
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
Example of a complete playbook for testing
- hosts: localhost
vars:
sqlversion:
stdout: "mysql Ver 14.14 Distrib 5.5.43, for Linux (x86_64) using readline 5.2"
w: "{{ sqlversion.stdout|split(',')|first|split()|last }}"
tasks:
- debug:
var: w
- debug:
msg: Version is lower than 5.6
when: w is version('5.6', '<')
If the filter split is not available use the method twice
w: "{{ (sqlversion.stdout.split(',')|first).split()|last }}"
... but I also am forced on this series of machines to use Ansible 2.9 ...
As mentioned by Vladimir Botka
If the filter split is not available use the method twice. I added an example. The filter split is available since 2.11
the split filter for manipulation strings is available as of Ansible v2.11 but you can just use the Python method .split().
As a workround in Ansible v2.9 or 2.10 one could also implement a Custom Filter Plugin and so a simple Ansible Jinja2 filter to split a string into a list. By doing this a filter like | split() can made be available in older versions.

How can I use regex in when condition

On an Ansible playbook, I'm trying to execute a shell command only if a service exist on the remote server.
I have 3 tasks :
service_facts
execution of shell command if tomcat is installed
display the output of the shell command if tomcat is installed
Here is my code :
- name: Get Infos
hosts: all
gather_facts: yes
become: false
remote_user: [MY_USER]
tasks:
- name: Get the list of services
service_facts:
- name: Get version of Tomcat if installed
become: true
shell: 'java -cp /opt/tomcat/lib/catalina.jar org.apache.catalina.util.ServerInfo | grep "Server version"'
register: tomcat_version
when: "'tomcat.service' in services"
- debug: msg="{{ tomcat_version.stdout_lines }}"
when: "'tomcat.service' in services"
The problem is on certains servers the service name is, for example, tomcat-8.1
How can i use regex in the when condition?
I tried regex(), regex_search(), either I'm doing it wrong or I don't know how to do it.
Have you any idea how to do it?
Thanks in advance!
Count matching items. For example
- service_facts:
- block:
- shell: smartctl --version | head -1
register: smart_version
- debug:
msg: "{{ smart_version.stdout_lines }}"
when: _srvcs|length > 0
vars:
_regex: '.*smart.*'
_srvcs: "{{ services|select('match', _regex) }}"
gives
msg:
- smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-73-generic] (local build)
The next option is to intersect the list of services, e.g.
when: _srvcs|length > 0
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
Debug
Q: "It gives me a failure on the server where my service doesn't exist, cause the playbook still tries to execute the shell. Is it normal?"
A: No. It is not normal. Print debug and find out why the condition evaluates to true, i.e. what service(s) match either the regex or the list. For example
- debug:
msg: |
_srvcs:
{{ _srvcs|to_nice_yaml|indent(2) }}
when: debug|d(false)|bool
vars:
my_services:
- smartmontools.service
- smart-8.1
- smart-devel.0.0.1
_srvcs: "{{ my_services|intersect(services) }}"
gives
msg: |-
_srvcs:
- smartmontools.service
To enable the task run the playbook with the option -e debug=true.

ansible playbook : Error to connect to a windows host (winrm) using aws secret manager

i try to connect my ansible host to a windows server using winrm
my ansible version :
ansible 2.10.8
config file = None
configured module search path = ['/home/ec2-user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/ec2-user/.local/lib/python3.6/site-packages/ansible
executable location = /home/ec2-user/.local/bin/ansible
python version = 3.6.8 (default, Dec 5 2019, 15:45:45) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)]
it work when the password is in vars
- hosts: win
vars:
ansible_user: "ansible"
ansible_password: "Itismypassword"
and it is not working with this configuration :
- hosts: win
vars:
ansible_user: "ansible"
ansible_password: "{{ lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true) | regex_search ('ansible_password\\\":\\\"(.*)\\\"','\\1')}}"
i retrieve the password ( i used a regex to get only the password, i'm not sure that is the correct way to do that)
when i want to use it i get this error:
fatal: [win_server]: FAILED! => {
"msg": "Invalid type for configuration option plugin_type: connection plugin: winrm setting: remote_password : Invalid type provided for \"string\": ['Itismypassword']"
}
thanks for your help !
The error message is pretty clear: list[str] != str; while I don't have an AWS Secret Manager to test against, thankfully the error message makes knowing the fix pretty obvious: grab the | first of the resulting list
ansible_password: "{{
lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true)
| regex_search ('ansible_password\\\":\\\"(.*)\\\"','\\1')
| first }}"
As for your aside, the answer is for sure not to use regex to extract those values; based on the regex you're using, I'm guessing the output of that aws_secret lookup is in fact JSON, and thus the correct behavior is to | from_json to turn it back into a dict (or list[dict]), then extract the key you want
something like this, but without knowing the actual shape this is likely not 100% correct:
ansible_password: >-
{{ lookup('amazon.aws.aws_secret', 'ansible_password', bypath=true)
| from_json | map(attribute='ansible_password') | first }}
thanks to mdaniel i progress in my understanding of ansible and aws secret manager: it's working with regex but i try to figure out how to do it in a proper way...
when i get secret from aws secret manager with this commands :
debug: msg="{{ lookup('amazon.aws.aws_secret', 'ansible_secret', bypath=true) | dict2items }} "
i have this answer :
"msg": [
{
"key": "ansible_secret",
"value": "{\"password\":\"itismypassword\",\"user\":\"ansible_user\"}"
}
]
i don't know how to get "itismypassword" from value field >< a jquery maybe ?

Ansible copy ssh public key from file, use in uri call

I need to copy the SSH public key from a local file, then use it in a uri task in my playbook.
Keep in mind, I cannot use "authorized_key" module as this is a system I must use the API to configure public keys for users.
Code below keeps failing, I am 100% sure its because of the filter I am using. I am including the commented out section that does work for the body.
Trying to use a lookup with a regex_search, I used [^\s]\s[^\s] which works in python. Also the key is in a different directory in my local host (../../ssh/ssh_key/key.pub)
Any ideas?
- name: copy public key to gitea
hosts: localhost
tasks:
- name: include user to add as variable
include_vars:
file: users.yaml
name: users
- name: Gather users key contents and create variable
# shell: "cat ../keys/ssh_keys/zz123z.pub | awk '{print $1 FS $2}'"
shell: "cat ../keys/ssh_keys/{{item.username}}.pub | awk '{print $1 FS $2}'"
register: key
with_items:
- "{{users.user}}"
- name: Add user's key to gitea
uri:
url: https://10.10.10.10/api/v1/admin/users/{{ item.username }}/keys
headers:
Authorization: "token {{ users.GiteaApiToken }}"
validate_certs: no
return_content: yes
status_code: 201
method: POST
body: "{\"key\": \"{{ key.stdout }}\", \"read_only\": true, \"title\": \"{{ item.username }} shared
body_format: json
with_items:
- "{{users.user}}"
This is the error I receive when using -vvv
TASK [Add user's key to gitea] *************************************************
task path: /home/dave/projects/Infrastructure/ansible/AddTempUsers/addusers.yaml:275
Wednesday 04 March 2020 18:14:29 -0500 (0:00:00.537) 0:00:01.991 *******
fatal: [localhost]: FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'stdout'\n\nThe error appears to be in '/home/dave/projects/Infrastructure/ansible/AddTempUsers/addusers.yaml': line 275, column 13, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n - name: Add user's key to gitea\n ^ here\n"
}
I FIGURED IT OUT!
used shell with an awk command to gather the keys. (Note: including an awk for RSA keys, and one for id_ed25519, which we use. RSA is commented out but others can comment if they wish to use.)
Used loop control to iterate through the results.
Code below:
- name: copy public key to gitea
hosts: localhost
tasks:
- name: include user to add as variable
include_vars:
file: users.yaml
name: users
- name: Gather users key contents and create variable
# For RSA Keys
# shell: "cat ../keys/ssh_keys/{{item.username}}.pub | awk '/-END PUBLIC KEY-/ { p = 0 }; p; /-BEGIN PUBLIC KEY-/ { p = 1 }'
# For id_ed5519 Keys
shell: "cat ../keys/ssh_keys/{{item.username}}.pub | awk '{print $1 FS $2}'"
register: key
with_items:
- "{{users.user}}"
- name: Add user's key to gitea
uri:
url: https://10.10.10.10/api/v1/admin/users/{{ item.username }}/keys
headers:
Authorization: "token {{ users.GiteaApiToken }}"
validate_certs: no
return_content: yes
status_code: 201
method: POST
body: "{\"key\": \"{{ key.results[ndx].stdout }}\", \"read_only\": true, \"title\": \"{{ item.username }} shared VM\"}"
body_format: json
with_items:
- "{{users.user}}"
loop_control:
index_var: ndx