This is definitely what parameterized roles (or even task includes) are intended for.
And no, you can’t pass in variables to templates like this.
You can use the ‘set_fact’ module however to change the values of variables if you need to, but I’d be inclined to use a parameterized task or role.
–Michael
The point is that both files are part of the same unit – they’d never be done in isolation, so using a parametrized role would definitely break encapsulation.
But a parametrized include should work since the task files inside a role are not intended to be run separately. It’s slightly more awkward than the example I gave, but not bad.
thanks,
Bryan
something like this should work for you:
- template: src=templ.j2 dest=dest{{item.suffix}}
with_items:
- { var: foo, suffix: 1}
- { var: bar, suffix: 2}
inside the template you need to use item.var instead of var
One of the goals of Ansible is to not make sure there are a lot of idioms to remember.
I think it would be conceptually more interesting to allow any arbitrary variables fed to the template module to be accepted into the variable namespace, but the downside is if you typo something like owner=username as ownr=username it doesn’t do what you want.
Given this is the first time I’ve seen a request for this, I’m inclined to say we don’t add anything just yet.
I’m really curious as to the underlying use case and why you are templating the file differently but reusing the template, but yet it’s not a role.
I thought task includes were working well for me, but I ran into another problem.
Here’s what I’ve got:
- include: template.yml listenPort=80 file=clara.conf socketPort={{ socket.port }} dev={{ env.development }}
- include: template.yml listenPort=4080 file=clara-test.conf port=4105 socketPort=10085 hostname=clara-test.local dev= test=true
when: env.development
The first line shouldn’t have to include socketPort={{ socket.port}}, but I had to do that because I couldn’t put something like socket={port: 4105} on the second line.
It works well for socketPort, but the same trick doesn’t work for the other variable, which is a boolean. I thought I had it figured out:
- include: template.yml listenPort=80 file=clara.conf socketPort={{ socket.port }} dev={{ env.development|default(‘’, true) }}
but that throws the error
File “/exo/ansible/lib/ansible/playbook/play.py”, line 280, in _load_tasks
(k,v) = t.split(“=”, 1)
ValueError: need more than 1 value to unpack
I worked around the problem by using a string comparison in the template.
I’m really curious as to the underlying use case and why you are templating the file differently but reusing the template, but yet it’s not a role.
We’re using ansible to deploy to vagrant & to production. For vagrant, the same service is available on two different ports with two slightly different configurations.
Bryan
Ok in this case, you don’t want to use two different tasks in the same playbook.
What you should do is put your production hosts in one group and your vagrant hosts in another group, and then specify the ports or other variables in group_vars. (You could also pass in the name of a variable file for vars_files with -e, like “-e mode=production”) and then:
vars_files:
assumes 1.2 syntax
But I like group_vars better.
The template can also leverage conditionals in Jinja2 for slightly different configurations.
There are several reasons why that doesn’t work.
1: the values for the first file are already coming from a file in the group_vars directory. It’s the second file that has to be different.
2: I want to use the same template for the second file. DRY means that nobody will forget to update one template file and not the other.
3: the group_vars file is generated from the conf.json file that our service uses to configure itself.
Bryan
I’m not seeing any reasons why it won’t work, but I’ll share some more details…
Answer to question 1 – If you need different variables for group 2 you could easily set them like so:
group_vars/prod
I’ll state my problem again.
production has a conf file with port 80.dev has a conf file with port 80 and another conf file with port 4080.
I want to use the same template to produce all three conf files. It’s a good rule of thumb: if dev & production use two different files, eventually somebody’s going to only update the dev file and things will break in production.
Bryan
I normally do this with:
production
ports:
devel
ports:
template
{% for port in ports %}
listen {{port}}
{% endfor %}
Or just have a flag that says “production” and have ifs in your template.
Also easy.
I think Bryan L is looking to have two separate files, not two ports in a single file… but Brian C, your reply yesterday is working for me doing a similar thing that came up this morning… i have a server that needs two files, coming from the same template, one ends up in /mysql/stage1/my.cnf and one in /mysql/stage2/my.cnf. I understand that there are different ways to model this with groups and roles, but following your advice from yesterday:
- template: src=my.cnf.j2 dest=/mysq/{{ item.name }}/my.cnf
with_items:
- { name: stage1, ip: 1.1.1.1, id: 5 }
- { name: stage2, ip: 2.2.2.2, id: 10 }
I had no idea that the “items” in with_items translated into variables you could use in your templates, but it works great… i can use {{ item.name }}, {{ item.ip }} and {{ item.id }} in my template as expected.
matt
There are TWO files produced on dev, from the SAME template.
How do you write an if statement that takes one branch the first time and the other branch the second time, if all of the variables that you pass into the template are the same for both instantiations of the template?
That’s why I want to pass in a variable to the template from the task. So that the template knows whether it’s the first instantiation or the second.
Bryan
Understood.
You could also consider using Jinja2 includes so that they reuse some common files.
I’m hesistant to allow templates to be parameterized due to the issue of being able to mistype a parameter that has meaning like ownre or moed and it not tell you that you mistyped the parameter.