Today I'm looking at #Ansible task that I'm pretty certain would be easier be easier with #Nix. It's a simple idea: I have a playbook where I went to setup multiple environments using the same Ansible role, but with different variable values set for each each environment. Each environment has their variables set in a different file. Ansible provides `vars_files` to load variables from a file and `include_role` with a `vars` attribute to set specific variables but... 🧵

... as far I can see, there's a way to load variables from a *file* in #Ansible that are then scoped to only one *role*. If you use `vars_files` in the playbook, the variabls get loaded globally, meaning the vars set for the first invocation of the role can pollute the invocation of the second role...

And with #Nix... well it's a real coding language with variable scopes and loops, so not can it handle this, but there's really nothing to learn if you already understand variable scopes. 🧵

I've seen this anti-pattern repeatedly in attempts to create useful mini-languages (aka DSLs). They appeared in templating languages, YAML, CMSes, and e-commerce software.

The irony is that they end up being complex enough that they are used by people who know a more capable coding language anyway, who then need to learn yet another micro-language only to discover it's incomplete, broken, underdocumented, or all three.

There are some genuinely useful DSLs, but many that fall short.
🧵

@markstos Not sure I follow what you're trying to do here, i read this as "I've written a playbook that I run once and it targets every device i have, I then configure PROD using one role, then TEST using a different role and then QA using a third role"

If that's a correct analysis, then I don't think you're using the tool as it's intended.

The way I use it is, in my inventory file I have Ansible groups for PROD, TEST and QA and I have group_var files for each of these groups.

I have a single playbook with tasks or roles for all the components I wish to configure.
When I execute the playbook, it checks to see which group the host is in and loads the appropriate variables. You can also use this group membership to include or exclude tasks as appropriate.

If you share your playbook, I could perhaps help further or I can thoroughly recommend @geerlingguy book "Ansible for DevOps" and his accompanying YouTube series.

@Fishd @geerlingguy Thanks for help. I should have clarified that all the environments are on the same host, a dev server with various test environments, and that’s the rub.

Though you give me an idea. I could assign an extra host name / inventory entry to the same dev-server for each env, then use the Ansible feature to have plays targeting different hosts within one playbook.

I think the vars would get scoped to the play and thus one environment that way.

@markstos If you use that inventory idea, you could reduce your plays to run the same tasks against each environment but pass in different variables for Prod, QA and Dev as appropriate.

So rather than have a play running against Prod with Task 1, 2 & 3 (with appropriate variables) only the then call the same tasks but against Test (with appropriate variables) and then again for QA ... you just have one set of plays, running against ALL hosts but passing in variables accordingly from their group membership.

I find it best to strip variables from playbooks and hold those at a host or group level and it's always good to remove duplication wherever possible.

@Fishd The role in question is made for re-use to be run against all envs (dev and qa on a test server, prod on prod cluster). I already use the inventory system as a centralized config system with minimal config elsewhere except for role defaults. I agree-- config is easiest to reason about and manage when it's centralized and not spread throughout the code.
@Fishd The solution I found is to split the playbook into multiple plays against the same host (no aliases needed), because both vars_files or include_vars are scoped to the play they are loaded into, not the playbook, preventing variables from bleeding from one role invocation to the next.

@markstos If you setup your inventory correctly, it's trivial to scope different variables to different environments and this allows for complex playbook / role execution.

Look into host_vars and group_vars ... if you apply vars at the host or group level, there is no need to specify them during a particular role, they're just there and customised for each device.

Inventory setup and variable precedence are one of the more complex parts to Ansible but are also one of its most powerful features.