Considering this Ansible task:
- name: Set some kernel parameters
lineinfile:
dest: "/home/cloud-user/{{item}}.hostsubnet.json"
regexp: '^(.*hostIP\".*)\"10.*\"(.*)$'
line: \1"{{hostvars['{{item}}']['ansible_eth0']['ipv4']['address']}}"\2
backrefs: yes
with_items: "{{ groups.nodes }}"
I am trying to loop over the all hosts in group [nodes].
For each node, I would like to fetch an IP from facts, and use that IP
as a substitute string for lineinfile regexp.
But this would not work. Looks like nested variables are not possible.
Is it possible to resolve the nested variable?
I am getting this error with a code:
fatal: [master-0.ocp-36-2.teco.dev.a.tecdomain.net]: FAILED! => {
"failed": true
}
MSG:
the field 'args' has an invalid value, which appears to include a
variable that is undefined. The error was:
'ansible.vars.hostvars.HostVars object' has no attribute '{{item}}'
The error appears to have been in
'/var/lib/ansible/playbooks/hostsubnet.yml': line 27, column 7, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
- name: Set some kernel parameters
^ here
You are trying refer to a key in hostvars using a string {{item}}.
You should use a variable name item instead:
line: \1"{{hostvars[item]['ansible_eth0']['ipv4']['address']}}"\2
You seem also to be confused with the terminology: you try to refer to a variable value inside a Jinja2 expression (i.e. what's wrapped in {{ }}). The answer to such a question is: by using variable name, plain.
Related
In this code block running ansible 2.9.6:
// System/group_vars/all/vars.yml
---
# AWS S3
# User Ansible
# Permissions put/get on arn:aws:s3:::snap/backup-db/*
# Plus d'infos: https://console.aws.amazon.com/iam/home?region=eu-west-2#/users/ansible?section=permissions
s3_access_key: {{ lookup('aws_ssm', '/iam/ansible/access-key', region='eu-west-2', aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) }}
Gives the following error:
ERROR! Syntax Error while loading YAML.
found unacceptable key (unhashable type: 'AnsibleMapping')
The error appears to be in 'System/group_vars/all/vars.yml': line 32, column 63, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
# Plus d'infos: https://console.aws.amazon.com/iam/home?region=eu-west-2
^ here
There appears to be both 'k=v' shorthand syntax and YAML in this task. Only one syntax may be used.
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
It thinks that region=eu-west-2 is a key value that is mixed with YML syntax.
What can I do to avoid Ansible throwing this wrongful error since it's in a YML comment ?
Thank you :)
It was a stupid mistake caused by a line right after this one:
# Plus d'infos: https://console.aws.amazon.com/iam/home?region=eu-west-2#/users/ansible?section=permissions
s3_access_key: {{ lookup('aws_ssm', '/iam/ansible/access-key', region='eu-west-2', aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) }}
Should be:
# Plus d'infos: https://console.aws.amazon.com/iam/home?region=eu-west-2#/users/ansible?section=permissions
s3_access_key: "{{ lookup('aws_ssm', '/iam/ansible/access-key', region='eu-west-2', aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) }}"
note the quotes around the "{{ lookup }}". The error message was trying to tell me so at the very last part of the error ("might be an issue with
missing quotes") I just was focused on why my comment was parsed and interpreted as key=value
I define a yml variable files for ansible with the following structure:
appserver:
root_directory: C:\app
config_directory: '{{ root_directory }}\config'
it seems the second variable config_directory cannot be interpreted correctly, I get a VARIABLE NOT FOUND ERROR.
I tried with:
appserver:
root_directory: C:\app
config_directory: '{{ appserver.root_directory }}\config'
It does not work either, I have a very long trace of error, the most interesting part is :
recursive loop detected in template string:{{ appserver.root_directory }}\config
When I use double quotes instead of simple quotes,
appserver:
root_directory: C:\app
config_directory: "{{ appserver.root_directory }}\config"
I get the following error:
The offending line appears to be:
app_root: D:\WynsureEnvironments\Application
wynsure_root: "{{ appserver.root_directory }}\config"
^ here
We could be wrong, but this one looks like it might be an issue with
missing quotes. Always quote template expression brackets when they
start a value. For instance:
with_items:
- {{ foo }}
Should be written as:
with_items:
- "{{ foo }}"
When using variable blocks, how can I reuse variables to assign new variables?
Thanks!
You cannot use such a recursive jinja2 variable declaration in ansible.
Here are 2 (non exhaustive list) alternative solutions:
Don't use a hash. Prepend your vars names. You will typically find this type of naming conventions in e.g. reusable roles on ansible galaxy
appserver_root_directory: C:\app
appserver_config_directory: '{{ appserver_root_directory }}\config'
If you really need a hash of this kind, declare a "private" variable outside of your hash and reuse it inside.
_appserver_root: C:\app
appserver:
root_directory: "{{ _appserver_root }}"
config_directory: "{{ _appserver_root }}\config"
In particular, I have a group_vars/all.yml file, with essentially the following contents:
my_foo: asdf
my_bar: '{{ my_foo }}'
If I later, while templating a file in a playbook, such as:
- name: template a file
template:
src: something.j2
dest: '....'
And in that something.j2 file, I use {{ my_bar }}, I somehow get asdf. How?
My prior understanding was that files such as group_vars/all.yml where essentially parsed as parse_yaml(parse_and_evaluate_jinja2(contents_of_the_file)). But if this were true, the line my_bar: '{{ my_foo }}' would not correctly evaluate: either we'd get an error, since my_foo is undefined to Jinja, or some default text, like "undefined", or ''. It's like the YAML was being parsed at the same time as the the Jinja2, which seems unlikely. Is that really what Ansible does? Or am I missing something else entirely?
(I couldn't find any exact documentation on how variable files are parsed in Ansibles docs, so if they exist, links are appreciated.)
My prior understanding was that files such as group_vars/all.yml where essentially parsed as parse_yaml(parse_and_evaluate_jinja2(contents_of_the_file))
Wrong. Ansible does all evaluation in runtime, for example:
You tell Ansible:
- debug:
msg: "{{ my_bar }}"
So when Ansible is going to pass msg parameter to debug module, it will do:
What is the parameter?
The parameter is {{ my_bar }}. Oh, it's a template and there's variable my_bar.
What is the my_bar's value?
The value is {{ my_foo }}. Oh, it's a template again, and there's variable my_foo.
What is the my_foo's value?
The value is asdf. Good! Nothing more to template.
So, my_bar's value is now asdf.
And parameter value is not asdf.
Let's print asdf.
By writing:
my_var: "{{ my_other_var }}"
you don't assign variables as in general programming, but write a simple template to calculate my_var's value in any given time (runtime), and not in time of parsing.
I am trying to split the variable based on delimiter. How can I achieve it?
some_module: {{item}}.split('#')[1]
with_items:
- git#someversionxxx
- gradle#someversionxxx
I get following error:
list object' has no attribute 'split ansible
I want to consider only first part of variable i.e. before '#'
some_module: "{{ item.split('#')[0] }}"
{{ ... }} is used to indicate Jinja2 expressions and everything you have is a Jinja2 expression
with YAML syntax in Ansible you must quote a string if it starts with { (unless it was a JSON object, here it's not)
the first element of the split result will have an index of 0
I am trying to read the contents of a file, store these in a variable and then insert them into another file if they don't already exist.
So, how I'm attempting to go about this is as follows:
# Create a variable that represents the path to the file that you want to read from
ssh_public_key_file: '../../jenkins_master/files/{{ hostvars[inventory_hostname]["environment"] }}/id_rsa.pub'
# Create a variable that represents the contents of this file:
ssh_public_key: "{{ lookup('file', '{{ ssh_public_key_file }}') }}"
I then use these variables in my Ansible playbook as follows:
- name: Install SSH authorized key
lineinfile: create=yes dest=~/.ssh/authorized_keys line=" {{ ssh_public_key }}" mode=0644
However, when I try and run the playbook, I get the following error message:
could not locate file in lookup: {{ ssh_public_key_file }}
Can anyone recommend a solution or suggest what I may have done wrong?
Thanks,
Seán
You have to change the line to:
# Create a variable that represents the contents of this file:
ssh_public_key: "{{ lookup('file', ssh_public_key_file) }}"
If you need to concatenate variables and strings you can do it like this:
# Example with two variables
ssh_public_key: "{{ lookup('file', var_1+var_2) }}"
# Example with string and variable
ssh_public_key: "{{ lookup('file', '~/config/'+var_1) }}"
. .
First I would make sure that your ssh_public_key_file variable is set up properly. If you add a task like the following what does it show?
- name: display variable
debug: var=ssh_public_key_file
If the output looks something like this then the variable isn't defined properly (eg. the "environment" fact doesn't exist for the host):
ok: [localhost] => {
"ssh_public_key_file": "../../jenkins_master/files/{{ hostvars[inventory_hostname][\"environment\"] }}/id_rsa.pub"
}
However if everything is defined properly then your output should show the variables replaced with their correct values:
ok: [localhost] => {
"ssh_public_key_file": "../../jenkins_master/files/foo/id_rsa.pub"
}
Once you've verified that then I would do the same thing with your ssh_public_key variable. Just output its value using the debug module. It should display as the contents of the public key file.
One other thing I would strongly suggest is to avoid using lineinfile altogether. Since you're working with SSH keys I would recommend you use the authorized_key module instead. It's a much cleaner way of managing authorized_keys files.