How can I write an ansible task that creates a file from a template such that a Jinja2 variable in the template’s source file is updated only the very first time that file is provisioned by ansible?
Consider the file my-service.conf
user = 'admin'
password = 'fxbff4w146lx813fjiead353fnpbfoqz'
some_other_option = 'yes'
What I want is for the password to be automatically generated at the time that the file is provisioned by ansible, so I create this Jinja2 template file my-service.conf.j2
user = 'admin'
password = '{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_lowercase', 'digits'], length=32) }}'
some_other_option = 'yes'
That will provision our config file with a random string as-desired, but the problem that I’m trying to avoid is that it will change in the future (eg if we change some_other_option
to no
in the future).
Therefore, I want to find a way such that the Jinja2 variable is substituted only when the file is first provisioned.
Note: This ansible role will be intentionally uploaded to the public internet. As such, passing a seed to the random generator would be terribly insecure, and it does not provide a valid solution to this question.
How can I conditionally replace a Jinja2 variable to never change on subsequent runs, without maintaining a state from the ansible controller(s)?
1
This doesn’t answer your question, but you might be able to achieve the same goal by writing the contents of the password
variable to a distinct file, if your configuration file supports Include
s.
Consider the following ansible task:
- name: "password.conf"
copy:
dest: "/etc/my-service/password.conf"
owner: root
group: root
mode: 0644
backup: yes
content: password = "{{ lookup('ansible.builtin.password', '/dev/null', chars=['ascii_lowercase', 'digits'], length=32) }}"
args:
creates: "/etc/my-service/password.conf"
- name: "my-service.conf"
template:
src: "my-service.conf.j2"
dest: "/etc/my-service/my-service.conf"
owner: root
group: root
mode: 0644
backup: yes
The first task creates a file named password.conf
, with a single line that contains a random 32-character password. Note that this file will only be created once, because of the creates
option in the argument. If the file /etc/my-service/password.conf
already exists, then ansible won’t run this task.
Then we can just have my-service.conf.j2
include our password file to instantiate the password
variable
# File: my-service.conf.j2
user = 'admin'
include '/etc/my-service/password.conf'
some_other_option = 'yes'
Now, you can happily change my-service.conf.j2
all you want, and ansible won’t change the password file’s contents.