Set fact with dynamic key name in ansible
Asked Answered
H

7

39

I am trying to shrink several chunks of similar code which looks like:

- ... multiple things is going here
  register: list_register
- name: Generating list
  set_fact: my_list="{{ list_register.results | map(attribute='ansible_facts.list_item') | list }}"

# the same code repeats...

The only difference between them is list name instead of my_list.

In fact, I want to do this:

set_fact:
  "{{ some var }}" : "{{ some value }}"

I came across this post but didn't find any answer here.

Is it possible to do so or is there any workaround?

Happening answered 1/7, 2016 at 10:56 Comment(0)
G
35

take a look at this sample playbook:

---
- hosts: localhost
  vars:
    iter:
      - key: abc
        val: xyz
      - key: efg
        val: uvw
  tasks:
    - set_fact: {"{{ item.key }}":"{{ item.val }}"}
      with_items: "{{iter}}"
    - debug: msg="key={{item.key}}, hostvar={{hostvars['localhost'][item.key]}}"
      with_items: "{{iter}}"
Gamecock answered 1/7, 2016 at 11:21 Comment(2)
In fact, the simplified answer would be set_fact: {"{{ key }}":"{{ val }}"}Happening
Thanks for the idea, I got around by using list of dictionaries [{key: var1, val: val1}, {key: var2, val: val2}] instead of a single dictionary {var1: val1, var2: val2}.Velate
C
20

The above does not work for me. What finally works is

- set_fact:
    example_dict: "{'{{ some var }}':'{{ some other var }}'}"

Which is in the end obvious. You construct a string (the outer double quotes) which is then interpreted as a hash. In hashes key and value must be single quotes (the inner single quotes around the variable replacements). And finally you just place your variable replacements as in any other string.

Stefan

Cleon answered 15/6, 2017 at 14:35 Comment(3)
Here, example_dict ends up being a string, not a dictionary.Velate
I had to define it this way as well, surrounded by double quotes, with ansible 2.3.2.0. It did not end up as a string, printing it with debug: showed it had the correct dictionary structure.Griffiths
Thanks a lot! This also works for the situation, where your environment variable key names are dynamic based on names just known at runtime. Therefore one can use environment: at module level described in the docs at docs.ansible.com/ansible/latest/user_guide/… and create the variables with - "{'dynamic environment variable key name inkl. {{ vars }}':'{{ dynamic environment variable value}}'}" (here´s a full example).Pyridine
C
7

As of 2018, using ansible v2.7.1, the syntax you suggest in your post works perfectly well.

At least in my case, I have this in role "a" :

- name: Set fact
  set_fact: 
     "{{ variable_name }}": "{{ variable_value }}"

And that in role "b" :

- debug:
  msg: "variable_name = {{ variable_name }}"

And execution goes :

TASK [role a : Set fact] *******************************************************
ok: [host_name] => {
  "ansible_facts": {
    "variable_name": "actual value"
  }, 
  "changed": false
}

...

TASK [role b : debug] **********************************************************
ok: [host_name] => {}

MSG:

variable_name = actual value
Creamy answered 11/11, 2018 at 9:31 Comment(1)
Using ansible 2.9.0 this does not work: - set_fact: _register_role_run: "{{ register_role_run_conf.name }}": "{{ register_role_run_conf.state }}" - debug: var: register_role_run_conf - debug: var: _register_role_run outputs TASK [register_role_run : debug] ok: [apigw-adco] => { "register_role_run_conf": { "name": "host_config", "state": true } } TASK [register_role_run : debug] ok: [apigw-adco] => { "_register_role_run": { "{{ register_role_run_conf.name }}": true } }Monochromat
R
4
- set_fact: '{{ some_var }}={{ some_value }}'

It creates a string of inline module parameter expression by concatenating value of some_var (fact name), separator = and value of some_value (fact value).

Rotl answered 3/4, 2018 at 2:9 Comment(2)
Nice, this actually works because in YAML the '=' notation is always a string and is parsed by ansible.Floozy
This works in Ansible 2.9.27Sixfold
M
1
- set_fact:
    var1={"{{variable_name}}":"{{ some value }}"}

This will create a variable "var1" with your dynamic variable key and value.


Example: I used this for creating dynamic tags in AWS Autoscaling group for creating kubernetes tags for the instances like this:

- name: Dynamic clustertag
  set_fact:
    clustertag={"kubernetes.io/cluster/{{ clustername }}":"owned"}
- name: Create the auto scale group
  ec2_asg:
    .
    .
    .
    tags:
      - "{{ clustertag }}"
Marasco answered 26/5, 2019 at 9:38 Comment(1)
doesn't work. set_fact: var1=... doesn't even parse.Erechtheus
V
1

Beware of a change in 2.9 – the behaviour changed rendering all the answers invalid. https://github.com/ansible/ansible/issues/64169

Vocoid answered 4/4, 2022 at 17:51 Comment(3)
What is the correct behavior then?Happening
I don't have an answer here – except maybe for creating a string with a JSON and importing it. I wanted to create a dictionary consisting of a single entry based on a dictionary with multiple app configurations – and the name of the app was stored in a variable. I resorted to looping over the original dictionary and calling the include with just one definition. But it's a workaround in code, not an alternative to the original problem. OTOH, I'd guess most of these cases can be converted to this approach.Bosk
That was a bug, and as the bug status closed indicates, it has been fixed.Headstream
T
1

My usecase is, to iterate over all items of a dictionary my_dict and update every item.

None of the answers above work for me, but this does:

- set_fact:
    my_dict:
      first:
        title: "First"
      second:
        title: "Second"

- set_fact:
    my_dict: "{{my_dict | combine({item.key:updated_item}, recursive=true) }}"
  vars:
    updated_item:
      name: "{{item.key}}"
  with_dict: "{{ my_dict }}"

Result:

"my_dict": {
    "first": {
         "name": "first",
         "title": "First"
    },
    "second": {
         "name": "second",
         "title": "Second"
    }
}
Transfusion answered 8/11, 2023 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.