# Services and TLS certificates - how to configure step by step?

**URL:** <https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483>\
**Category:** Get Help\
**Tags:** playbook\
**Created:** [September 14, 2025, 10:10am UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483 "2025-09-14T10:10:44Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![zeropage](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/zeropage/32/5607_2.png) [@zeropage](https://forum.ansible.com/u/zeropage)\
**Post date:** [September 14, 2025, 10:10am UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/1 "2025-09-14T10:10:44Z")

</div>

My CA issues certificates for my TLS-encrypted connections. For example, for rsyslog, stunnel, or Prometheus node exporter. I configure all these services with Ansible roles. However, I have a fundamental problem with the process, especially when a service is run root-less.

If my playbook obtains the certificate first, the user with which the service is executed is not yet known. However, this user must become the owner of the certificate key files.  
Conversely, the role for the service cannot be fully executed because a certificate that does not yet exist would lead to a runtime error.

Without Ansible as an administrator, I would perform the process in three steps:

- Install and configure the service.
- Obtain and install the certificate.
- Configure the certificate files for the service.

What is the best way to automate such a scenario?

Thanks in advance.

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [September 15, 2025, 3:09pm UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/2 "2025-09-15T15:09:08Z")

</div>

I’m not sure I 100% understand what you’re describing because the scenario you’re outlining is a bit too generic to really grasp what your issue is, and from what I gather you already have the solution in your post.

My understanding is that you want to have an Ansible playbook install an application or service, then after install, you want to install CA-signed SSL certs for the app, but you’re running in to a chicken-and-egg scenario regarding certificate ownership since the service-specific accounts don’t exist until the app is installed.

> Without Ansible as an administrator, I would perform the process in three steps:
> 
> - Install and configure the service.
> - Obtain and install the certificate.
> - Configure the certificate files for the service.

As far as I can figure, you’d do this same step-by-step in an Ansible playbook. Hence my confusion as to what your question is, as it seems like you already have the answer. You’d probably want to run some checks before obtaining and installing the cert just so you don’t send the same cert to your CA to be signed in order to keep idempotency in Ansible.

---

<div class="post-metadata">

**Author:** ![zeropage](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/zeropage/32/5607_2.png) [@zeropage](https://forum.ansible.com/u/zeropage)\
**Post date:** [September 16, 2025, 7:11am UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/3 "2025-09-16T07:11:53Z")

</div>

> [@anon81884396](#):
>
> but you’re running in to a chicken-and-egg scenario regarding certificate ownership since the service-specific accounts don’t exist until the app is installed.

Yes, that’s what I mean. 😀

> [@anon81884396](#):
>
> You’d probably want to run some checks before obtaining and installing the cert just so you don’t send the same cert to your CA to be signed in order to keep idempotency in Ansible.

I don’t know how to implement this in the playbook. Until now, I have always integrated all roles one after the other so that each role is executed exactly once:

```auto
- hosts: all
  ...
  roles:
    - name: myroles.x509
      tags: [x509]
    - name: prometheus.prometheus.node_exporter
      tags: [prometheus, node_exporter]

```

The role x509 obtains a server certificate and assigns the key file to the group ssl-cert and permissions u=rw,g=r. The role node\_exporter creates the user node\_exp, configures the path to the server certificate, and starts the service. An error occurs because the user node\_exp does not yet belong to the group ssl-cert.

Now the opposite sequence:

```auto
  roles:
    - name: prometheus.prometheus.node_exporter
      tags: [prometheus, node_exporter]
    - name: myroles.x509
      tags: [x509]

```

The node exporter is first configured without TLS encryption. Then a certificate is obtained. The third step would be to switch the node\_exporter role from unencrypted to TLS. But how does that work? In my inventory, I only have one set of variables for exactly one point in time.

I am very eager to hear your response, especially since I have encountered this problem before in similar cases.

---

<div class="post-metadata">

**Author:** ![bvitnik](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/bvitnik/32/4132_2.png) [@bvitnik](https://forum.ansible.com/u/bvitnik)\
**Post date:** [September 17, 2025, 10:36pm UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/4 "2025-09-17T22:36:57Z")

</div>

You can cheat a little bit by pre-creating the `node-exp` user (and group). Looking at the code of `prometheus.prometheus.node_exporter` role, the user is created with this simple task:

```auto
- name: "Create system user {{ _common_system_user }}"
  ansible.builtin.user:
    name: "{{ _common_system_user }}"
    system: true
    shell: "/usr/sbin/nologin"
    group: "{{ _common_system_group }}"
    home: "{{ _common_config_dir | default('/', true) }}"
    create_home: false

```

If we resolve the variables and their default values, this becomes:

```auto
- name: "Create system user node-exp"
  ansible.builtin.user:
    name: "node-exp"
    system: true
    shell: "/usr/sbin/nologin"
    group: "node-exp"
    home: "/etc/node_exporter" # You should verify this, I'm not sure if I traced the value correctly
    create_home: false

```

Your playbook than becomes:

```auto
- hosts: all
  ...
  pre_tasks:
  - name: "Create system user node-exp"
    ansible.builtin.user:
      name: "node-exp"
      system: true
      shell: "/usr/sbin/nologin"
      group: "node-exp"
      home: "/etc/node_exporter"
      create_home: false

  roles:
    - name: myroles.x509
      tags: [x509]
    - name: prometheus.prometheus.node_exporter
      tags: [prometheus, node_exporter]

```

Your `myroles.x509` role should change the ownership of SSL private key and cert to the `node-exp` user that now exists. `prometheus.prometheus.node_exporter` will just confirm that the user already exists and do nothing additional (idempotence).

---

<div class="post-metadata">

**Author:** ![zeropage](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/zeropage/32/5607_2.png) [@zeropage](https://forum.ansible.com/u/zeropage)\
**Post date:** [September 22, 2025, 1:24pm UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/5 "2025-09-22T13:24:20Z")

</div>

> [@bvitnik](#):
>
> You can cheat a little bit by pre-creating the `node-exp` user (and group).

Thank you for your explanation. But I had already thought of that idea. However, it’s just a cheat. And it doesn’t answer my question.

Another idea was to work with the ansible.builtin.include\_role module (not tested). I fear there will be a ping pong effect between the first and second include of the role:

```auto
- name: Initially configue node exporter
  ansible.builtin.include_role:
    name: prometheus.prometheus.node_exporter
- name: Issue and deploy certificate
  ansible.builtin.include_role:
    name: myroles.x509
- name: Configure TLS
  ansible.builtin.include_role:
    name: prometheus.prometheus.node_exporter
  vars:
    node_exporter_tls_server_config:
      cert_file: /etc/node_exporter/tls.cert
      key_file: /etc/node_exporter/tls.key

```

There are probably other approaches. As is often the case, I am interested in best practice. A permanent and universally applicable solution.

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [September 22, 2025, 1:45pm UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/6 "2025-09-22T13:45:01Z")

</div>

I exclusively use “include\_role” for the reasons you’re encountering, it gives greater control over when and how a role is executed and with what vars and all that. I didn’t even know you could do “roles:” to include them in bulk like you showed. I can see how that is problematic for going “back” and reconfiguring an installed app.

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [September 25, 2025, 9:21am UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/7 "2025-09-25T09:21:12Z")

</div>

The `roles` keyword has been there from the start of ansible. I don’t know if it is officially deprecated but IMHO it should be. The order of execution is always confusing users when combined with `tasks`, `pre_tasks`, and `post_tasks`. While that is only confusing, the impossibility of running tasks between roles is (for me, at least) a reason to stay away from the `roles` keyword 🙂

---

<div class="post-metadata">

**Author:** ![zeropage](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/zeropage/32/5607_2.png) [@zeropage](https://forum.ansible.com/u/zeropage)\
**Post date:** [September 26, 2025, 4:02pm UTC](https://forum.ansible.com/t/services-and-tls-certificates-how-to-configure-step-by-step/44483/8 "2025-09-26T16:02:10Z")

</div>

I wasn’t aware that the role keyword should not be used. I will revise my playbooks accordingly.
