# Template with to\_ini in a loop: 'str' object has no attribute 'items'

**URL:** <https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007>\
**Category:** Get Help\
**Tags:** ini, template, variables, ee\
**Created:** [November 19, 2024, 8:44pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007 "2024-11-19T20:44:29Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 19, 2024, 8:44pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/1 "2024-11-19T20:44:29Z")

</div>

I’m trying to generate some files using `ansible.builtin.template` and I would like to use the `community.general.to_ini` jinja filter.  
The problem is that I have an error ` The error was: ansible.errors.AnsibleFilterError: to_ini failed to parse given dict:'str' object has no attribute 'items'. 'str' object has no attribute 'items'`

My code is the following

```auto
- name: Configure action                                                                                                                                                                                                                                                  
  become: yes
  ansible.builtin.template:
    src: "{{ action_file_tmpl }}"  
    dest: "{{ [actions_directory, action.name] | path_join }}" 
    owner: root
    group: root
    mode: '0600'
    vars:
      name: "{{ action.name }}"
      config: "{{ action.config }}"                                                                                                                                                                                                                                                     
    loop: "{{ daks_backupninja_actions }}"
    loop_control:
      loop_var: action
      label: "{{ name }}"

```

and the template file contains

```auto
# some header
{{ config | community.general.to_ini }}

```

The complete error message is

```auto
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.errors.AnsibleFilterError: to_ini failed to parse given dict:'str' object has no attribute 'items'. 'str' object has no attribute 'items'
failed: [debian-12] (item=10-test.sh) => {"action": {"config": {"bl": "environment", "ee": "value"}, "name": "10-test.sh"}, "ansible_loop_var": "action", "changed": false, "msg": "AnsibleFilterError: to_ini failed to parse given dict:'str' object has no attribute 'items'. 'str' object has no attribute 'items'"}

```

I tried several solutions (like using an intermediate task file and loop over it giving the correct var) but everytime I got this error. It looks like my vars become a string and I can’t loop over it with `to_ini`.

I don’t understand what is the correct way to do it.

ps: I could probably use `community.general.ini_file` but I would like to have more control over my file, like add an header as seen in the example.

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 19, 2024, 8:53pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/2 "2024-11-19T20:53:12Z")

</div>

I’m trying to get my head around what you are trying to do, what does the `daks_backupninja_actions` list look like?

---

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 19, 2024, 9:00pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/3 "2024-11-19T21:00:55Z")

</div>

currently just

```auto
vars:
  daks_backupninja_actions:
    - name: 10-test.sh 
      config:
         bl: environment
         ee: value

```

but at the end something like this:

```auto
   - name: 90-remote.rdiff
     config:
        source:
           type: local
           include:
             - /etc
             - /var
           exclude:
            - /var/lock    
           keep: 300D      
        dest:
           type: remote
           host: host.example.com
           user: backup-user
           directory: /path/to/backups       
           sshoptions: -o IdentityFile=/home/backup-user/.ssh/backup_id

```

to generate

```auto
[source]
include = /etc
include = /var
exclude = /var/lock
keep = 300D
type = local

[dest]
type = remote
host = host.example.com
user = backup-user
directory = /path/to/backups
sshoptions = -o IdentityFile=/home/backup-user/.ssh/backup_id

```

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 19, 2024, 9:12pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/4 "2024-11-19T21:12:57Z")

</div>

I think you might need to add a [dict2items](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/dict2items_filter.html) filter to the mix, I’m doing this kind of thing for [php](https://git.coop/webarch/php) and [systemd](https://git.coop/webarch/systemd), perhaps like this in the template?

```yaml
{{ config | ansible.builtin.dict2items | community.general.to_ini }}

```

---

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 19, 2024, 9:16pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/5 "2024-11-19T21:16:37Z")

</div>

you mean in the task or in the template?

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 19, 2024, 9:19pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/6 "2024-11-19T21:19:47Z")

</div>

Sorry, template, I’ve edited my answer above, also you could check the `type` like this in the template:

```auto
{{ config | ansible.builtin.type_debug }}

```

But I’m tired and could well have got the wrong 🙄

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 19, 2024, 9:34pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/7 "2024-11-19T21:34:43Z")

</div>

Actually ignore what I wrote above, since the `ini` files have sections I think you need a more complicated template? I have [one like this](https://git.coop/webarch/php/-/blob/main/templates/php.j2):

```auto
; {{ ansible_managed }}
{% if php_conf_file_proposed_sections | bool %}
{% for php_section in php_conf_file_proposed_vars | dict2items %}
[{{ php_section.key }}]
{% for php_variable_pair in php_section.value | dict2items %}
{% if php_conf_file_no_extra_spaces | bool %}
{{ php_variable_pair.key }}={{ php_variable_pair.value }}
{% else %}
{{ php_variable_pair.key }} = {{ php_variable_pair.value }}
{% endif %}
{% endfor %}
{% endfor %}
{% else %}
{% for php_conf_pair in php_conf_file_proposed_vars | ansible.builtin.dict2items %}
{% if php_conf_file_no_extra_spaces | bool %}
{{ php_conf_pair.key }}={{ php_conf_pair.value }}
{% else %}
{{ php_conf_pair.key }} = {{ php_conf_pair.value }}
{% endif %}
{% endfor %}
{% endif %}
; vim: syntax=dosini

```

To generate `ini` files from lists like this:

```yaml
  - name: PHP 8.3 configuration
    version: "8.3"
    state: present
    files:
      - name: PHP 8.3 CLI configuration
        path: /etc/php/8.3/cli/php.ini
        state: present
        conf:
          apc:
            "apc.coredump_unmap": "0"
            "apc.enable_cli": "1"
            "apc.enabled": "1"
            "apc.entries_hint": "4096"
            "apc.gc_ttl": "3600"
            "apc.serializer": "php"
            "apc.shm_segments": "1"
            "apc.shm_size": "32M"
            "apc.slam_defense": "1"
            "apc.ttl": "0"
            "apc.use_request_time": "0"
          "CLI Server":
            "cli_server.color": "1"
          MySQLi:
            "mysqli.allow_local_infile": "0"
            "mysqli.allow_persistent": "1"
            "mysqli.default_port": '3306'
            "mysqli.max_links": "-1"
            "mysqli.max_persistent": "-1"
            "mysqli.reconnect": "0"
            "mysqli.default_socket": "/run/mysqld/mysqld.sock"

```

However when I wrote this role `community.general.to_ini` didn’t exist… so perhaps all the above can be ignored!

I posted some links to `ini` resources for Ansible here, I don’t know if these will help:

> [@Call to action: Send us your "see also" suggestions](http://forum.ansible.com/t/call-to-action-send-us-your-see-also-suggestions/4093/2):
>
> Great idea, I’d also like to see more examples adding to pages perhaps another thread for that would make sense? INI / CONF The .ini / .conf file lookup, fliters and modules could be linked: [community.general.ini\_file module](https://docs.ansible.com/ansible/latest/collections/community/general/ini_file_module.html)[ansible.builtin.ini lookup](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/ini_lookup.html)[community.general.to\_ini filter](https://docs.ansible.com/ansible/latest/collections/community/general/to_ini_filter.html)[community.general.from\_ini filter](https://docs.ansible.com/ansible/latest/collections/community/general/from_ini_filter.html) And also the JC INI parsers that can be used with [the JC filter](https://docs.ansible.com/ansible/latest/collections/community/general/jc_filter.html): [jc.parsers.ini](https://kellyjonbrazil.github.io/jc/docs/parsers/ini)[jc.parsers.ini\_dup](https://kellyjonbrazil.github.io/jc/docs/parsers/ini_dup) And perhaps the [Wikipedia INI page](https://en.wikipedia.org/wiki/INI_file) and a list of some key applications tha…

Sorry I’m too knackered tonight to help any more but if you are still stuck later this week I’m sure I could help solve it!

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 19, 2024, 9:58pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/8 "2024-11-19T21:58:50Z")

</div>

Actually this [MariaDB template](https://git.coop/webarch/mariadb/-/blob/master/templates/mariadb.j2) is a better (simpler) example:

```auto
# {# j2lint: disable=jinja-statements-indentation j2lint: disable=single-statement-per-line #}
{% for mariadb_cnf_section in mariadb_cnf.conf | dict2items %}
[{{ mariadb_cnf_section.key }}]
{% for mariadb_cnf_variable_pair in mariadb_cnf_section.value | dict2items %}
{{ mariadb_cnf_variable_pair.key }}{% if mariadb_cnf_variable_pair.value is defined %} = {{ mariadb_cnf_variable_pair.value }}{% endif %}
{% endfor %}
{% endfor %}
# vim: syntax=dosini

```

With this [list of vars](https://git.coop/webarch/mariadb/-/blob/master/defaults/main.yml?):

```yaml
mariadb_config:
  - name: MariaDB server configuration
    path: /etc/mysql/mariadb.conf.d/50-server.cnf
    state: edited
    conf:
      mysqld:
        binlog_format: "ROW"
        ignore_db_dir: "lost+found"
        innodb_buffer_pool_instances: "1"
        innodb_buffer_pool_size: "256M"
        innodb_log_file_size: "64M"
        join_buffer_size: "8M"
        key_buffer_size: "16M"
        max_allowed_packet: "64M"
        max_connections: "80"
        max_heap_table_size: "32M"
        max_user_connections: "0"
        open_files_limit: "122880"
        query_cache_limit: "0"
        query_cache_size: "0"
        query_cache_type: "0"
        skip_name_resolve:
        slow_query_log_file: "/var/log/mysql/mariadb-slow.log"
        table_cache: "64"
        table_open_cache: "4000"
        thread_concurrency: "10"
        tmp_table_size: "32M"
        transaction_isolation: "READ-COMMITTED"
        performance_schema: "1"

```

But again this role was written before the `community.general.to_ini` filter so I fear I’m just adding noise rather than helping at this point… ☹

---

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 20, 2024, 6:43am UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/9 "2024-11-20T06:43:44Z")

</div>

This means doing it ‘manually’ without using the `to_ini` filter.  
I had thought about it and I may need to do it this way, given my data structure.

But I really want to be able to use (or at least test) the `to_ini` filter and understand why it doesn’t work in my example. I may want (or need) to use it in another use case.

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 20, 2024, 12:29pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/10 "2024-11-20T12:29:01Z")

</div>

I’ve just tested updating my [fail2ban role](https://git.coop/webarch/fail2ban), which currently uses the [community.general.ini\_file module](https://docs.ansible.com/ansible/latest/collections/community/general/ini_file_module.html), to update it to [use a template](https://git.coop/webarch/fail2ban/-/blob/1b16c0813ae2df6893c18cba9d67655b01c8d38d/tasks/conf_file.yml#L62) which is [like the one you have](https://git.coop/webarch/fail2ban/-/blob/1b16c0813ae2df6893c18cba9d67655b01c8d38d/templates/fail2ban.conf.j2) and the [community.general.to\_ini filter](https://docs.ansible.com/ansible/latest/collections/community/general/to_ini_filter.html) and it seems fine, other than the fact that booleans are capitalized when previously I have set them to to be lower case, I’m not sure if this is OK so [I’ve asked in the fail2ban discussion form](https://github.com/fail2ban/fail2ban/discussions/3886) — so I haven’t applied this update yet, I hope I can as it will simplify the role quite a lot.

Could you post your whole backupninja role somewhere so I can run and debug it without having to recreate it — I’m confident I can help you get it working.

---

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 20, 2024, 2:59pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/11 "2024-11-20T14:59:53Z")

</div>

Nice 🙂  
I’ll look at your code in a few days I hope. I’m busy with other things in the mean time.  
I’ll comment here if I’m still confused or my code stills has errors.  
Thanks

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [November 20, 2024, 4:05pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/12 "2024-11-20T16:05:03Z")

</div>

The first problem is indentation. Everything below “`mode: '0600'`” needs to be shifted two columns to the left.

The second problem is that your simplified `daks_backupninja_actions` variable has no “section” level(s). The spurious message about the string having no items is because the underlying python module is trying to parse the value of the “`bl`” section as a dict, but its value is the string “environment”. Throw in another level between “`config`” and “`bl`”. (Ironically, your “but at the end something like this” data contains the “section” levels missing from the simplified data.)

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 20, 2024, 6:24pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/13 "2024-11-20T18:24:36Z")

</div>

> [@utoddl](#):
>
> your simplified `daks_backupninja_actions` variable has no “section” level(s)

Thanks for spotting that, I have written a couple of (probably terrible!) sets of Ansible tasks in the past to detect if “section” level heading are present or not — the `from_ini` filter doesn’t support files without sections so I’m not surprised that the `to_ini` filter doesn’t either, it would be nice if they did…

[https://github.com/ansible-collections/community.general/issues/7889](https://github.com/ansible-collections/community.general/issues/7889)

---

<div class="post-metadata">

**Author:** ![daks](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/daks/32/5277_2.png) [@daks](https://forum.ansible.com/u/daks)\
**Post date:** [November 25, 2024, 4:35pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/14 "2024-11-25T16:35:11Z")

</div>

@utoddl you’re right about the indentation! but it’s just bad copy-paste and it’s ok in my code 🙂

About the format, in fact my test was too simple and the other is better.  
It works (quite) well.

I now have the confirmation that I will need to do the processing myself simply because I want lists to be processed in a certain way (see `include`).

Nevermind thanks for your help both!

---

<div class="post-metadata">

**Author:** ![chris](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/chris/32/293_2.png) [@chris](https://forum.ansible.com/u/chris)\
**Post date:** [November 25, 2024, 4:49pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/15 "2024-11-25T16:49:44Z")

</div>

Fair enough, FWIW I have now updated [this fail2ban role](https://git.coop/webarch/fail2ban) to use `community.general.to_ini` to [template ini / conf files](https://git.coop/webarch/fail2ban/-/blob/master/templates/fail2ban.conf.j2).

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/ansible/original/1X/1882d9a01da5d8996a862359b0913cc568a39b84.svg) [@system](https://forum.ansible.com/u/system)\
**Post date:** [December 25, 2024, 4:49pm UTC](https://forum.ansible.com/t/template-with-to-ini-in-a-loop-str-object-has-no-attribute-items/11007/16 "2024-12-25T16:49:53Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
