Trying not to repeat vars but keep roles general purpose.

Hello,

I have been using Ansible in production for about a year now. I use it for both configuration management and deployments. I created a bunch of cruft since I was learning Ansible at the time and now trying to clean that up during a refactoring of the playbooks. So one issue I see in my playbooks is that the roles I created were very specific to my vars and are not general purpose (I would not be able to publish said role to Ansible Galaxy). To have general purpose roles the vars those roles except must verbose. So for example I have applications that need to be load balanced. Those applications have runtime parameters such as specific port number that need to be defined in vars files and the load balancer also needs to know about those same port numbers and the number of rules needed. I really really want to define those port variables once and only once, then mix those into the general purpose vars to allow the loadbalancer to know about those ports and rules. If I change the port number for some reason in my application vars file then I don’t need to worry about also changing them in other vars files.

I started looking for a solution for this. I see there is string interpolation in the yml files which would allow you to do some sharing but does not really solve the problem where the loadbalancer vars structure is different than the applications vars structure. So I was able to find a solution to the issue, however it feels very janky. Ansible’s templating module gives me a way to combine multiple vars sources into a new single vars file. I ended up creating a var/templates and vars/generated folders in my vars folder. In the template file I can describe how I want to combine the vars together to generate a new vars file. Then in my playbook I can run the template module locally to generate a vars file to work with my load balancer module. The result is a verbose loadbalancer.yml file, something that I would not want to maintain by hand, but keeps the loadbalancer role general purpose and if I did add another application into my applications vars or changed ports or names, I would not have to worry about the loadbalancer.yml file.

I think the templating vars will work ok, but again feel kind of wrong. I’m not sure what the better solution is. Maybe some kind of way to extend roles with some kind of vocabulary to combine multiple vars into a single variable? Any suggestions or feedback on how to do this a better way would be great.

Cheers!

A lot of what Galaxy is a starting point, or people wanting to make a configuration that is very generic as an example, so I think that’s ok in many cases to find your roles, adapted to your business, are that kind of way.

To a great extent, though, things like load balancing can usually be handled by pre_tasks or post_tasks, or having other roles for those actions, so you can often include that outside the part that configures application foo.

I generally discourage any sort of playbook run that generates a variable file, and would rather see in many cases Ansible just looping over that existing data. So it may be a question of how that data is structured and why a particular variable might not be easy to pass into a role parameter?

I’m trying to draw the right parallels here but it’s a bit harder to grasp without seeing some of the playbooks.

Part of me wants to say “be ok that your configuration is specific to your infrastructure”, because that’s how configuration always winds up - Galaxy content provides a leg up when starting or when wanting to show a configuration to someone, but almost always will end up with your own customizations.

That’s the way it’s happened with other config tools, and I think that’s ok.

When you say combining vars into multiple variables, that’s a bit more of a tactical issue, and one I think I can understand.

There’s been a natural desire to avoid a lot of programming constructs in Ansible, but given enough need, the arising of some of them is something I’m willing to explore.

In this case, it feels like this might be the “extends” operator in Python, and some of this may be leverageable with set_fact today and using Jinja2 filters, though it may not be optimal.

Your response sounds like it’s on the right track. So the gist is that you advocate roles that are tailored to your infrastructure and my attempts to generate vars is not the right approach.

I think the pain I’m feeling is the lack of programming constructs in Ansible. I dislike the specific configuration at the role level feels like I go too many folders deep define that logic. The advantage I saw with generating vars is that the specifics configuration is closer to the main level vars file. However I can’t say this approach feels any simpler than doing specifics at the roles level.

Anyway, I’m just nitpicking. Ansible is been a very flexible and powerful tool. Thanks again for the great response.

One logical addition I’d love is the ability to extend a list in inventory in a child group. As far as I know, there’s no easy way to do this.

Something like:

all.yml
rpms:

  • gcc
  • make

child.yml
rpms:
{{rpms}}

  • bison

and then rpms on a host in child group would contain gcc, make and bison

Anyway, not an emergency, just a nice to have.

Will

One logical addition I’d love is the ability to extend a list in inventory in a child group. As far as I know, there’s no easy way to do this.

Something like:

all.yml
rpms:

  • gcc
  • make

child.yml
rpms:
{{rpms}}

  • bison

and then rpms on a host in child group would contain gcc, make and bison

Have you tried

rpms: "{{ base_rpms|union(['bison']) }}"

Ansible has a recursive evaluation guard, but that will give you a list, not a string, according to a quick sanity check.