Variable for loop in playbook

I’m using a playbook to create users and add pubkeys on a server, it does it sequentially and the usernames, groups and keys are all in order. I’m wondering if there is a better way to do this, follow me on some pseudo playbook code:

Ok, so in Ansible, it’s more of an exercise of modeling than coding. We intentionally do not encourage things like nested Ruby lambda blocks :slight_smile:

with_items is used for loops.

Still, I would restructure things a bit so you have a nice list instead of a hash table, so that the name of your user is easy to get at.

Right, I missed a ‘item’ there, thanks :slight_smile:

Ok, so in Ansible, it’s more of an exercise of modeling than coding. We intentionally do not encourage things like nested Ruby lambda blocks :slight_smile:

I thought so, but it was an example from a chef cookbook ;).

with_items is used for loops.

Still, I would restructure things a bit so you have a nice list instead of a hash table, so that the name of your user is easy to get at.

===

  • name: user1
    full_name: User1
    groups:
  • a
  • b
  • name: user2

That’s a better idea indeed.

(I would also be much more apt to include the users definition in a group_vars/ file rather than keeping it inline in the playbook, but that’s not material to this)

Correct, is what I normally do, but to keep it simple for this post I did it like above.

===

Using 1.2 syntax:

  • name: make some users
    user: name={{ item.name }} groups={{ “,”.join(items.groups) }}
    with_items: users

Or before:

  • name: make some users
    user: name=${item.name} groups=${item.groups}
    with_items: ${users}

Now for keys, I wouldn’t keep them in a list based on user, but based on a list of what keys are authorized on what system or group.

keys:

  • user: bob
    key: /path/to/bob.key
  • user: alice
    key: /path/to/alice.key

So your task is just like this:

  • name: authorized keys setup
    authorized_key: name={{ keys.user }} key=“{{ lookup(‘file’, key.path }}”
    with_items: keys

You could keep the key in the variable structure too, I just prefer them on disk so they are easier to replace.

And I’d also keep this “keys” list as part of something like “group_vars/mygroup” so I could control who had access to what system based on group, rather than keeping that list in my playbook in the vars section also.

Yet again thanks for the help :slight_smile:

Have two related questions. When using the following syntax for groups:

users:

  • name: “rve”
    full_name: “RvE”
    groups:
  • admin
  • amsterdam

And the following playbook command:

  • name: Create groups
    group: name=${item.groups} state=present
    with_items: ${users}

Results in the following error:

msg: groupadd: ‘admin,amsterdam’ is not a valid group name

Is that because I access or define the variable in a wrong way?

Second one, regarding the SSH keys:

Playbook variables:

users:

  • name: “rve”
    full_name: “RvE”
    ssh_keys:
  • “ssh-rsa AAAAB3NzaC1yc2EAA[…] rve1@box”
  • “ssh-rsa AAAAB3NzaC1yc2EAA[…] rve2@box”
  • “ssh-rsa AAAAB3NzaC1yc2EAA[…] rve3@box”

Playbook command:

  • name: add ssh keys
    authorized_key: user=${item.name} key=“${item.ssh_keys}”
    with_items: ${users}

Ends up in the authorized key file as a comma separated list:
ssh-rsa AAAAB3NzaC1yc2EAA[…] rve1@box, ssh-rsa AAAAB3NzaC1yc2EAA[…] rve2@box, ssh-rsa AAAAB3NzaC1yc2EAA[…] rve3@box

is the above because I’m using a with_items: users and it should for this case be a with_items: users.$USERNAME.groups/ssh_keys? if so, how can I fix it?

Groups are set on the ‘user’ module, and created with the groups module, so you’ve got that a bit inverted. See the module docs for details.

As for SSH keys the way you are modelling them, I would highly recommend constructing the authorized keys file based on a template.

This came up yesterday actually.

Groups are set on the ‘user’ module, and created with the groups module, so you’ve got that a bit inverted. See the module docs for details.

Before adding a user to a groups I need to create the group. I want to create all the groups that are defined in the variables for the users, and not define a seperate group variable with all possible groups. Therefore I want to access all the users.USERNAME.groups in a with_items, but that doesn’t seem to work.

As for SSH keys the way you are modelling them, I would highly recommend constructing the authorized keys file based on a template.

Correct, been doing that for my playbooks, but still I was wondering how to access the nested variables in a with_items.

“Before adding a user to a groups I need to create the group. I want to create all the groups that are defined in the variables for the users, and not define a seperate group variable with all possible groups. Therefore I want to access all the users.USERNAME.groups in a with_items, but that doesn’t seem to work.”

Sorry, this is presently not possible.

I’ll think about how to make a lookup plugin that allows this kind of easy iteration.

Jinja2 in playbooks is quite useful, but it doesn’t allow the kind of list comprehensions you want to do right now.

So for now, you’ll have to have two lists.

You are reading the 1.2 documentation, you should look for the 1.1 documentation at http://ansible.cc/docs/released/1.1/

I will update the 1.2 “getting started” information to make it clear also, rather than just listing it on the home page.

Maybe arbitrary Python list comprehensions with a couple of helper functions would be a nice solution for such advanced loops (just like advanced condition checking is done with with is done with an arbitrary Python expression). Jinja2 unfortunately does not support them.

Greetings,
gw

Yep, that’s what I was proposing on IRC this AM. Perhaps something I may explore this weekend.