I have an issue with a playbook, and wonder if the issue is that some key names in the dictionary I use have special character, namely, a dash in them. Ansible is not clear wether this is allowed or not.
The tasks:
- debug:
var: block_storage_route
- debug:
var: block_storage_route[0].table-id
vars:
block_storage_route:
- "table-id": 0
Output:
TASK [debug] ******************************************************
ok: [example.com] => {
"block_storage_route": [
{
"table-id": 0
}
]
}
TASK [debug] ******************************************************
ok: [example.com] => {
"block_storage_route[0].table-id": "VARIABLE IS NOT DEFINED!"
}
As you can see I get a "VARIABLE IS NOT DEFINED" error, although the key table-id is present in the debug of the variable block_storage_route.
Why is that?
Q: "Are special characters allowed in dictionary keys in Ansible?"
A: Yes. They are. In fact, there are no restrictions in YAML on the keys. Quoting from Mapping (aka Python dictionary)
"The content of a mapping node is an unordered set of key: value node pairs, with the restriction that each of the keys is unique. YAML places no further restrictions on the nodes. In particular, keys may be arbitrary nodes, ..."
Use bracket notation instead of the dot notation when the name of the key doesn't comply with Ansible Creating valid variable names
- debug:
var: block_storage_route[0]['table-id']
Related
I have an issue with a playbook, and wonder if the issue is that some key names in the dictionary I use have special character, namely, a dash in them. Ansible is not clear wether this is allowed or not.
The tasks:
- debug:
var: block_storage_route
- debug:
var: block_storage_route[0].table-id
vars:
block_storage_route:
- "table-id": 0
Output:
TASK [debug] ******************************************************
ok: [example.com] => {
"block_storage_route": [
{
"table-id": 0
}
]
}
TASK [debug] ******************************************************
ok: [example.com] => {
"block_storage_route[0].table-id": "VARIABLE IS NOT DEFINED!"
}
As you can see I get a "VARIABLE IS NOT DEFINED" error, although the key table-id is present in the debug of the variable block_storage_route.
Why is that?
Q: "Are special characters allowed in dictionary keys in Ansible?"
A: Yes. They are. In fact, there are no restrictions in YAML on the keys. Quoting from Mapping (aka Python dictionary)
"The content of a mapping node is an unordered set of key: value node pairs, with the restriction that each of the keys is unique. YAML places no further restrictions on the nodes. In particular, keys may be arbitrary nodes, ..."
Use bracket notation instead of the dot notation when the name of the key doesn't comply with Ansible Creating valid variable names
- debug:
var: block_storage_route[0]['table-id']
I have the following json data to work with:
"result": {
"json": {
"licences": {
"4216": {
"license": "4512-5421-5134-7413"
}
}
}
}
I first tried to get my expected value with the following debug task:
- name: Display Licence ID
ansible.builtin.debug:
var: result.json.licences[0].license
But it returns :
"VARIABLE IS NOT DEFINED!"
As a work-around I'm currently using :
- name: Display Licence ID
ansible.builtin.debug:
var: result.json.licences[result.json.licences|first].license
This works but isn't there a better way than repeating the whole variable name?
Your input data is effectively a dict but you are trying to use it as a list. There is no element named 0 (i.e. having a key which value is 0) in your dict called licences (and since it is not a list, there is no first element at index 0 either).
What I understand from your question is that your licences dict will always contain a single key and that you want to get the licence number inside that key whatever the key name is. Please edit your question to be more specific if my understanding is wrong.
One way to do this is:
- name: Display licence ID
debug:
msg: "{{ result.json.licences | dict2items | map(attribute='value.license') | first }}"
Explanation:
the dict2items filter transforms your result.json.licenses dict
{
"4216": {
"license": "4512-5421-5134-7413"
}
}
into a list of key/value dicts
[
{
"key": "4216",
"value": {
"license": "4512-5421-5134-7413"
}
}
]
the map(attribute='value.license)` filter extracts the nested attribute from each element into a list
[
"4512-5421-5134-7413"
]
the first filter keeps only the first element from that list:
"4512-5421-5134-7413"
If you ever have several results and want to get a list of license number, just remove the first filter at the end. You can also build up on this example to get the key for the license if you need it one day.
Create the complete path. Attribute '4216' is a string. It must be quoted. Put it into the brackets
- debug:
var: result.json.licences['4216'].license
vars:
result:
json:
licences:
'4216':
license: 4512-5421-5134-7413
gives
result.json.licences['4216'].license: 4512-5421-5134-7413
If you don't know the number, or, in general, the name of the attribute, use json_query. For example,
- debug:
var: result.json.licences|json_query('*.license')|first
As a side note: If the attribute were a number 4216 you could have used it without quotes and brackets. For example,
- debug:
var: result.json.licences.4216.license
vars:
result:
json:
licences:
4216:
license: 4512-5421-5134-7413
gives
result.json.licences.4216.license: 4512-5421-5134-7413
For details see Referencing key:value dictionary variables.
I have a simple vlan config file which I like to have the keys to match with JunOS syntax (this way I can pass them as aggrogate if I need to), so I'm using the vlan-id key like this example variable file.
# vlans.yaml
vlans:
- name: general
description: "General"
vlan-id: 100
- name: hotline
description: "Accounting"
vlan-id: 110
but i can't access the vlan-id key because of the hyphen
- debug:
msg: "{{ item.vlan-id }}"
loop: "{{ vlans }}"
tags: debug
"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'vlan'
if I output the item directly I can see the key
- debug:
msg: "{{ item }}"
loop: "{{ vlans }}
ok: [SW02] => (item={'name': 'external', 'description': 'External', 'vlan-id': 209}) => {
"msg": {
"description": "External",
"name": "external",
"vlan-id": 209
}
}
ok: [SW01] => (item={'name': 'external', 'description': 'External', 'vlan-id': 209}) => {
"msg": {
"description": "External",
"name": "external",
"vlan-id": 209
}
}
Any ideas how to solve this?
Put the attributes into the brackets '[]' aka array notation. For example
msg: "{{ item['vlan-id'] }}"
Quoting from Ansible allows dot notation and array notation for variables. Which notation should I use?
If your variable contains dots (.), colons (:), or dashes (-), if a key begins and ends with two underscores, or if a key uses any of the known public attributes, it is safer to use the array notation.
Q: "This is not a variable, it's the key."
A: Right. The only restriction is the keys are unique. Quoting from YAML 1.2
Mapping. The content of a mapping node is an unordered set of key: value node pairs, with the restriction that each of the keys is unique. YAML places no further restrictions on the nodes. In particular, keys may be arbitrary nodes, the same node may be used as the value of several key: value pairs, and a mapping could even contain itself as a key or a value (directly or indirectly).
register: dnsfact
- debug: var=dnsfact.ansible_facts.azure_dnszones[0].name
If I debug like above, I am getting the below output:
ok: [openshift-infra01.example.net] => {
"dnsfact.ansible_facts.azure_dnszones[0].name": "226********"
debug: var=dnsfact.ansible_facts.azure_dnszones[1].name > this will give me two values.
debug: var=dnsfact.ansible_facts.azure_dnszones[2].name > this will give me three values.
If I want to print all the values, which value do I need to pass? I tried with dnszones[:] and dnszones[':']. But I am not able to fetch the values.
please find the actual output below.
"dnsfact.ansible_facts.azure_dnszones": [
{
"etag": "00000002-0000-0000-9ed1-be810a8bd401",
"id": "/subscription*****/dnszones/226.10.in-addr.arpa",
"location": "global",
"name": "226.10.in-addr.arpa",
From this output we are trying to filter the "name".
You'll want the map jinja2 filter:
- debug:
msg: "{{ dnsfact.ansible_facts.azure_dnszones | map(attribute="name") | list }}"
Its job is to do almost the same thing that python's map does, but more targeted toward attribute extraction than just arbitrary computation over a list. You'll (usually) need that | list at the end because map() is lazy and returns a generator, which needs to be evaluated if you just want to see the output
When map'ing a attribute in a list of variables, Ansible is adding a 'Undefined' to the beginning and end of the key.
The variables:
vault_config_listener_params:
- address: "0.0.0.0:8200"
- tls_cert_file: "/etc/ssl/certs/wildcard.crt"
- tls_key_file: "/etc/ssl/certs/wildcard.key"
The debug task:
- debug: var=vault_config_listener_params|map(attribute="tls_cert_file")|list
The output:
ok: [id70118] => {
"vault_config_listener_params|map(attribute=\"tls_cert_file\")|list":
"[Undefined, u'/etc/ssl/certs/wildcard.crt', Undefined]"
}
The maping seems to have worked, as the key path has been extracted. But where are the 'Undefined' coming from?
PS: The variables needs to be a list, as they are looped it in another place with jinja2.
First of all, don't use debug's var when printing arbitrary expressions, use msg instead.
As for your question, map is quite dumb and doesn't do what you don't ask it to, so you actually need to select items with specified attributes defined first, and then get its values:
- debug:
msg: "{{ vault_config_listener_params | selectattr('tls_cert_file','defined') | map(attribute='tls_cert_file') | list }}"