From 37e9589f9a91f6d47bb1c14cffcbe2b3c88bbaa6 Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Fri, 25 Sep 2026 17:19:33 +0200 Subject: [PATCH 1/3] beats: resolve the output hosts once instead of in every template Closes #557 --- changelogs/fragments/beats_output_hosts.yml | 10 ++++++++ roles/beats/README.md | 4 +++- roles/beats/meta/argument_specs.yml | 20 ++++++++++++++-- roles/beats/tasks/main.yml | 26 ++++++++++++++++----- roles/beats/templates/auditbeat.yml.j2 | 18 +++----------- roles/beats/templates/filebeat.yml.j2 | 18 +++----------- roles/beats/templates/metricbeat.yml.j2 | 18 +++----------- 7 files changed, 60 insertions(+), 54 deletions(-) create mode 100644 changelogs/fragments/beats_output_hosts.yml diff --git a/changelogs/fragments/beats_output_hosts.yml b/changelogs/fragments/beats_output_hosts.yml new file mode 100644 index 00000000..cf170653 --- /dev/null +++ b/changelogs/fragments/beats_output_hosts.yml @@ -0,0 +1,10 @@ +minor_changes: + - The beats role takes ``beats_elasticsearch_hosts`` and ``beats_logstash_hosts``, so the two + output types can point at different hosts, and so the hosts can be set in a full stack at all, + where ``beats_target_hosts`` has no effect. Left unset they resolve the way the templates did + before, from the inventory group in a full stack and from ``beats_target_hosts`` standalone. + +trivial: + - The three Beats templates read the resolved host lists instead of working the hosts out + themselves, which removes nine inventory lookups and the ``elasticstack_full_stack`` branch + around each of them. diff --git a/roles/beats/README.md b/roles/beats/README.md index 18d43373..2f312545 100644 --- a/roles/beats/README.md +++ b/roles/beats/README.md @@ -39,7 +39,9 @@ Run only parts of the role with `--tags`: | `beats_filebeat`
**Type**: `bool`
**Default**: `true` | Install and manage Filebeat. | | `beats_auditbeat`
**Type**: `bool`
**Default**: `false` | Install and manage Auditbeat. | | `beats_metricbeat`
**Type**: `bool`
**Default**: `false` | Install and manage Metricbeat. | -| `beats_target_hosts`
**Type**: `list` of `str`
**Default**: `['localhost']` | Hosts the Beats ship to. Only used when the role runs standalone; with the other Elastic Stack roles the targets are determined automatically. | +| `beats_target_hosts`
**Type**: `list` of `str`
**Default**: `['localhost']` | Hosts the Beats ship to when the role runs standalone; with the other Elastic Stack roles the targets come from the inventory. Applies to whichever output a Beat uses, so set beats_elasticsearch_hosts or beats_logstash_hosts instead when the two outputs need different hosts. | +| `beats_elasticsearch_hosts`
**Type**: `list` of `str` | Elasticsearch hosts the Beats ship to. Defaults to the nodes from the elasticsearch group, or to beats_target_hosts when the role runs standalone. | +| `beats_logstash_hosts`
**Type**: `list` of `str` | Logstash hosts the Beats ship to. Defaults to the nodes from the logstash group, or to beats_target_hosts when the role runs standalone. | | `beats_fields`
**Type**: `list` of `str` | Global fields added to the log and syslog (tcp/udp) inputs (not to the mysql, journald or docker inputs), given as a list of "key: value" strings. This is the global counterpart to the per-input fields. Unset by default. See the Filebeat inputs documentation. | | `beats_logging`
**Type**: `str`
**Default**: `"file"` | Where the Beats log. Set to "file" to log into beats_logpath; any other value leaves the Beats built-in logging. | | `beats_loglevel`
**Type**: `str`
**Default**: `"info"` | Log level for all Beats. | diff --git a/roles/beats/meta/argument_specs.yml b/roles/beats/meta/argument_specs.yml index ab9988e6..460ab183 100644 --- a/roles/beats/meta/argument_specs.yml +++ b/roles/beats/meta/argument_specs.yml @@ -46,8 +46,24 @@ argument_specs: default: - localhost description: >- - Hosts the Beats ship to. Only used when the role runs standalone; with - the other Elastic Stack roles the targets are determined automatically. + Hosts the Beats ship to when the role runs standalone; with the other Elastic + Stack roles the targets come from the inventory. Applies to whichever output a + Beat uses, so set beats_elasticsearch_hosts or beats_logstash_hosts instead when + the two outputs need different hosts. + + beats_elasticsearch_hosts: + type: list + elements: str + description: >- + Elasticsearch hosts the Beats ship to. Defaults to the nodes from the + elasticsearch group, or to beats_target_hosts when the role runs standalone. + + beats_logstash_hosts: + type: list + elements: str + description: >- + Logstash hosts the Beats ship to. Defaults to the nodes from the logstash + group, or to beats_target_hosts when the role runs standalone. beats_fields: type: list diff --git a/roles/beats/tasks/main.yml b/roles/beats/tasks/main.yml index 1be84161..02ae9cdb 100644 --- a/roles/beats/tasks/main.yml +++ b/roles/beats/tasks/main.yml @@ -65,15 +65,30 @@ - renew_ca - renew_beats_cert -- name: Check that the inventory has the groups the Beats outputs need +- name: Set the Elasticsearch hosts the Beats ship to + ansible.builtin.set_fact: + beats_elasticsearch_hosts: >- + {{ groups[elasticstack_elasticsearch_group_name] | default([]) + if elasticstack_full_stack | bool else beats_target_hosts }} + when: beats_elasticsearch_hosts is undefined + +- name: Set the Logstash hosts the Beats ship to + ansible.builtin.set_fact: + beats_logstash_hosts: >- + {{ groups[elasticstack_logstash_group_name] | default([]) + if elasticstack_full_stack | bool else beats_target_hosts }} + when: beats_logstash_hosts is undefined + +- name: Check that every enabled Beat has hosts to ship to ansible.builtin.assert: - that: groups[beats_output_group] | default([]) | length > 0 + that: beats_output_hosts | length > 0 fail_msg: >- - {{ item.variable }} is set to "{{ item.output }}", so the hosts come from the inventory - group "{{ beats_output_group }}", which does not exist or is empty. Add that group to - your inventory. + {{ item.variable }} is set to "{{ item.output }}", but no hosts are known for it. In a full + stack they come from the inventory group "{{ beats_output_group }}", standalone from + beats_target_hosts. quiet: true vars: + beats_output_hosts: "{{ beats_logstash_hosts if item.output == 'logstash' else beats_elasticsearch_hosts }}" beats_output_group: "{{ elasticstack_logstash_group_name if item.output == 'logstash' else elasticstack_elasticsearch_group_name }}" loop: - variable: beats_filebeat_output @@ -88,7 +103,6 @@ loop_control: label: "{{ item.variable }}" when: - - elasticstack_full_stack | bool - item.enabled | bool - item.output in ['logstash', 'elasticsearch'] diff --git a/roles/beats/templates/auditbeat.yml.j2 b/roles/beats/templates/auditbeat.yml.j2 index 2485e741..b2a688f1 100644 --- a/roles/beats/templates/auditbeat.yml.j2 +++ b/roles/beats/templates/auditbeat.yml.j2 @@ -41,31 +41,19 @@ setup.kibana: {% if beats_auditbeat_output == "elasticsearch" %} output.elasticsearch: {% if beats_security | bool %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] username: "elastic" password: "{{ beats_writer_password.stdout }}" ssl.enabled: true ssl.verification_mode: none ssl.certificate_authorities: ["/etc/beats/certs/ca.crt"] {% else %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] {% endif %} {% endif %} {% if beats_auditbeat_output == "logstash" %} output.logstash: -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_logstash_group_name] %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_logstash_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] loadbalance: {{ beats_auditbeat_loadbalance }} {% if beats_security | bool %} ssl.enabled: true diff --git a/roles/beats/templates/filebeat.yml.j2 b/roles/beats/templates/filebeat.yml.j2 index a7a89cfc..453d8def 100644 --- a/roles/beats/templates/filebeat.yml.j2 +++ b/roles/beats/templates/filebeat.yml.j2 @@ -119,31 +119,19 @@ setup.kibana: {% if beats_filebeat_output == "elasticsearch" %} output.elasticsearch: {% if beats_security | bool %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] username: "elastic" password: "{{ beats_writer_password.stdout }}" ssl.enabled: true ssl.verification_mode: none ssl.certificate_authorities: ["/etc/beats/certs/ca.crt"] {% else %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] {% endif %} {% endif %} {% if beats_filebeat_output == "logstash" %} output.logstash: -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_logstash_group_name] %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_logstash_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] loadbalance: {{ beats_filebeat_loadbalance }} {% if beats_security | bool %} ssl.enabled: true diff --git a/roles/beats/templates/metricbeat.yml.j2 b/roles/beats/templates/metricbeat.yml.j2 index 2218fd15..373146ab 100644 --- a/roles/beats/templates/metricbeat.yml.j2 +++ b/roles/beats/templates/metricbeat.yml.j2 @@ -16,31 +16,19 @@ setup.kibana: {% if beats_metricbeat_output == "elasticsearch" %} output.elasticsearch: {% if beats_security | bool %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"https://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] username: "elastic" password: "{{ beats_writer_password.stdout }}" ssl.enabled: true ssl.verification_mode: none ssl.certificate_authorities: ["/etc/beats/certs/ca.crt"] {% else %} -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_elasticsearch_group_name] %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_elasticsearch_hosts %}"http://{{ host }}:{{ elasticstack_elasticsearch_http_port }}"{% if not loop.last %},{% endif %}{% endfor %}] {% endif %} {% endif %} {% if beats_metricbeat_output == "logstash" %} output.logstash: -{% if elasticstack_full_stack | bool %} - hosts: [ {% for host in groups[elasticstack_logstash_group_name] %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% else %} - hosts: [ {% for host in beats_target_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] -{% endif %} + hosts: [ {% for host in beats_logstash_hosts %}"{{ host }}:{{ elasticstack_beats_port }}"{% if not loop.last %},{% endif %}{% endfor %}] loadbalance: {{ beats_metricbeat_loadbalance }} {% if beats_security | bool %} ssl.enabled: true From c056d7188ee7a5ffe0dfe51f3055f047eb2a6b1a Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Wed, 7 Oct 2026 14:42:01 +0200 Subject: [PATCH 2/3] beats: remove beats_target_hosts and keep the host resolution working with --tags --- changelogs/fragments/beats_output_hosts.yml | 12 ++++++--- molecule/beats_default/converge.yml | 2 ++ molecule/beats_extended/converge.yml | 2 ++ roles/beats/README.md | 5 ++-- roles/beats/defaults/main.yml | 2 -- roles/beats/meta/argument_specs.yml | 25 ++++++++---------- roles/beats/tasks/main.yml | 28 +++++++++++++++------ 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/changelogs/fragments/beats_output_hosts.yml b/changelogs/fragments/beats_output_hosts.yml index cf170653..66adb868 100644 --- a/changelogs/fragments/beats_output_hosts.yml +++ b/changelogs/fragments/beats_output_hosts.yml @@ -1,8 +1,14 @@ +breaking_changes: + - The beats role no longer takes ``beats_target_hosts``. When ``elasticstack_full_stack`` is + false, set ``beats_elasticsearch_hosts`` or ``beats_logstash_hosts`` instead, depending on the + output the Beats use. Without them the run stops with a message instead of shipping to + ``localhost``. + minor_changes: - The beats role takes ``beats_elasticsearch_hosts`` and ``beats_logstash_hosts``, so the two - output types can point at different hosts, and so the hosts can be set in a full stack at all, - where ``beats_target_hosts`` has no effect. Left unset they resolve the way the templates did - before, from the inventory group in a full stack and from ``beats_target_hosts`` standalone. + output types can point at different hosts. When ``elasticstack_full_stack`` is true they can be + set as well, and otherwise get the hosts of the inventory groups named in + ``elasticstack_elasticsearch_group_name`` and ``elasticstack_logstash_group_name``. trivial: - The three Beats templates read the resolved host lists instead of working the hosts out diff --git a/molecule/beats_default/converge.yml b/molecule/beats_default/converge.yml index 1d984bfd..9b412799 100644 --- a/molecule/beats_default/converge.yml +++ b/molecule/beats_default/converge.yml @@ -10,6 +10,8 @@ vars: elasticstack_ci: true elasticstack_full_stack: false + beats_logstash_hosts: + - localhost elasticsearch_jna_workaround: true elasticsearch_disable_systemcallfilterchecks: true elasticstack_release: "{{ lookup('env', 'ELASTIC_RELEASE') | int}}" diff --git a/molecule/beats_extended/converge.yml b/molecule/beats_extended/converge.yml index 449fbc5d..1ef8f67b 100644 --- a/molecule/beats_extended/converge.yml +++ b/molecule/beats_extended/converge.yml @@ -25,6 +25,8 @@ elasticsearch_jna_workaround: true elasticsearch_disable_systemcallfilterchecks: true elasticstack_full_stack: false + beats_logstash_hosts: + - localhost elasticstack_no_log: false beats_filebeat_mysql_slowlog_input: true beats_auditbeat: true diff --git a/roles/beats/README.md b/roles/beats/README.md index 2f312545..7287c99f 100644 --- a/roles/beats/README.md +++ b/roles/beats/README.md @@ -39,9 +39,8 @@ Run only parts of the role with `--tags`: | `beats_filebeat`
**Type**: `bool`
**Default**: `true` | Install and manage Filebeat. | | `beats_auditbeat`
**Type**: `bool`
**Default**: `false` | Install and manage Auditbeat. | | `beats_metricbeat`
**Type**: `bool`
**Default**: `false` | Install and manage Metricbeat. | -| `beats_target_hosts`
**Type**: `list` of `str`
**Default**: `['localhost']` | Hosts the Beats ship to when the role runs standalone; with the other Elastic Stack roles the targets come from the inventory. Applies to whichever output a Beat uses, so set beats_elasticsearch_hosts or beats_logstash_hosts instead when the two outputs need different hosts. | -| `beats_elasticsearch_hosts`
**Type**: `list` of `str` | Elasticsearch hosts the Beats ship to. Defaults to the nodes from the elasticsearch group, or to beats_target_hosts when the role runs standalone. | -| `beats_logstash_hosts`
**Type**: `list` of `str` | Logstash hosts the Beats ship to. Defaults to the nodes from the logstash group, or to beats_target_hosts when the role runs standalone. | +| `beats_elasticsearch_hosts`
**Type**: `list` of `str` | Elasticsearch hosts the Beats ship to. When elasticstack_full_stack is true and this variable is not set, the role fills it with the hosts of the inventory group named in elasticstack_elasticsearch_group_name. When elasticstack_full_stack is false, set it yourself as soon as a Beat uses the elasticsearch output, otherwise the run stops. | +| `beats_logstash_hosts`
**Type**: `list` of `str` | Logstash hosts the Beats ship to. When elasticstack_full_stack is true and this variable is not set, the role fills it with the hosts of the inventory group named in elasticstack_logstash_group_name. When elasticstack_full_stack is false, set it yourself as soon as a Beat uses the logstash output, otherwise the run stops. | | `beats_fields`
**Type**: `list` of `str` | Global fields added to the log and syslog (tcp/udp) inputs (not to the mysql, journald or docker inputs), given as a list of "key: value" strings. This is the global counterpart to the per-input fields. Unset by default. See the Filebeat inputs documentation. | | `beats_logging`
**Type**: `str`
**Default**: `"file"` | Where the Beats log. Set to "file" to log into beats_logpath; any other value leaves the Beats built-in logging. | | `beats_loglevel`
**Type**: `str`
**Default**: `"info"` | Log level for all Beats. | diff --git a/roles/beats/defaults/main.yml b/roles/beats/defaults/main.yml index 8d1bd6c7..f80ed9ea 100644 --- a/roles/beats/defaults/main.yml +++ b/roles/beats/defaults/main.yml @@ -4,8 +4,6 @@ beats_security: false beats_filebeat: true beats_auditbeat: false beats_metricbeat: false -beats_target_hosts: - - localhost beats_logging: file beats_logpath: /var/log/beats beats_loglevel: info diff --git a/roles/beats/meta/argument_specs.yml b/roles/beats/meta/argument_specs.yml index 460ab183..dc2b960e 100644 --- a/roles/beats/meta/argument_specs.yml +++ b/roles/beats/meta/argument_specs.yml @@ -40,30 +40,25 @@ argument_specs: description: Install and manage Metricbeat. # ----- General ----- - beats_target_hosts: - type: list - elements: str - default: - - localhost - description: >- - Hosts the Beats ship to when the role runs standalone; with the other Elastic - Stack roles the targets come from the inventory. Applies to whichever output a - Beat uses, so set beats_elasticsearch_hosts or beats_logstash_hosts instead when - the two outputs need different hosts. - beats_elasticsearch_hosts: type: list elements: str description: >- - Elasticsearch hosts the Beats ship to. Defaults to the nodes from the - elasticsearch group, or to beats_target_hosts when the role runs standalone. + Elasticsearch hosts the Beats ship to. When elasticstack_full_stack is true and + this variable is not set, the role fills it with the hosts of the inventory group + named in elasticstack_elasticsearch_group_name. When elasticstack_full_stack is + false, set it yourself as soon as a Beat uses the elasticsearch output, otherwise + the run stops. beats_logstash_hosts: type: list elements: str description: >- - Logstash hosts the Beats ship to. Defaults to the nodes from the logstash - group, or to beats_target_hosts when the role runs standalone. + Logstash hosts the Beats ship to. When elasticstack_full_stack is true and this + variable is not set, the role fills it with the hosts of the inventory group + named in elasticstack_logstash_group_name. When elasticstack_full_stack is false, + set it yourself as soon as a Beat uses the logstash output, otherwise the run + stops. beats_fields: type: list diff --git a/roles/beats/tasks/main.yml b/roles/beats/tasks/main.yml index 02ae9cdb..3f5c5717 100644 --- a/roles/beats/tasks/main.yml +++ b/roles/beats/tasks/main.yml @@ -69,27 +69,34 @@ ansible.builtin.set_fact: beats_elasticsearch_hosts: >- {{ groups[elasticstack_elasticsearch_group_name] | default([]) - if elasticstack_full_stack | bool else beats_target_hosts }} + if elasticstack_full_stack | bool else [] }} when: beats_elasticsearch_hosts is undefined + tags: always - name: Set the Logstash hosts the Beats ship to ansible.builtin.set_fact: beats_logstash_hosts: >- {{ groups[elasticstack_logstash_group_name] | default([]) - if elasticstack_full_stack | bool else beats_target_hosts }} + if elasticstack_full_stack | bool else [] }} when: beats_logstash_hosts is undefined + tags: always - name: Check that every enabled Beat has hosts to ship to ansible.builtin.assert: - that: beats_output_hosts | length > 0 + that: __beats_output_hosts | length > 0 fail_msg: >- - {{ item.variable }} is set to "{{ item.output }}", but no hosts are known for it. In a full - stack they come from the inventory group "{{ beats_output_group }}", standalone from - beats_target_hosts. + {{ item.variable }} is set to "{{ item.output }}", but {{ __beats_output_hosts_variable }} is + empty. {{ __beats_output_source }} quiet: true vars: - beats_output_hosts: "{{ beats_logstash_hosts if item.output == 'logstash' else beats_elasticsearch_hosts }}" - beats_output_group: "{{ elasticstack_logstash_group_name if item.output == 'logstash' else elasticstack_elasticsearch_group_name }}" + __beats_output_hosts: "{{ beats_logstash_hosts if item.output == 'logstash' else beats_elasticsearch_hosts }}" + __beats_output_hosts_variable: "{{ 'beats_logstash_hosts' if item.output == 'logstash' else 'beats_elasticsearch_hosts' }}" + __beats_output_group: "{{ elasticstack_logstash_group_name if item.output == 'logstash' else elasticstack_elasticsearch_group_name }}" + __beats_output_source: >- + {{ 'elasticstack_full_stack is true, so it is filled from the inventory group "' + ~ __beats_output_group ~ '" unless you set it.' + if elasticstack_full_stack | bool + else 'elasticstack_full_stack is false, so you have to set it.' }} loop: - variable: beats_filebeat_output enabled: "{{ beats_filebeat }}" @@ -105,6 +112,11 @@ when: - item.enabled | bool - item.output in ['logstash', 'elasticsearch'] + tags: + - configuration + - beats_configuration + - beats_filebeat_configuration + - beats_auditbeat_configuration - name: Import Filebeat tasks ansible.builtin.import_tasks: filebeat.yml From f8b23cb8b5ca1d054e6a422465acfa75ac64c75c Mon Sep 17 00:00:00 2001 From: Afeef Ghannam Date: Wed, 7 Oct 2026 17:21:44 +0200 Subject: [PATCH 3/3] Document which tags a task gets --- CONTRIBUTING.md | 8 ++++++++ changelogs/fragments/beats_output_hosts.yml | 2 ++ 2 files changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dcd9b59..e04d918b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -89,6 +89,14 @@ Modules document themselves through the `DOCUMENTATION`, `EXAMPLES` and `RETURN` Shared code under `plugins/module_utils/` has no such block, because `ansible-doc` does not read it. Those functions are described in [plugins/module_utils/README.md](plugins/module_utils/README.md), please keep it current when you change them. +## Tags + +Every tag listed in a role README has to work on its own. `--tags ` must not fail because a task it depends on was skipped. + +* A `set_fact` task that only builds variables and cannot fail gets `tags: always`. The variables then exist in every run, and the task does not need to be touched when other tasks that read them get new tags. +* A task that can stop the run, such as `assert` or `fail`, gets the same tags as the tasks it protects. +* A task that changes the host and registers a result other tasks read gets the tags of those tasks. + ## Testing Besides testing your change against a real setup, the repository has Molecule scenarios that exercise the whole stack, and it checks for ansible-lint and yamllint errors. To run the linters locally there is a `makefile`: diff --git a/changelogs/fragments/beats_output_hosts.yml b/changelogs/fragments/beats_output_hosts.yml index 66adb868..976c7da3 100644 --- a/changelogs/fragments/beats_output_hosts.yml +++ b/changelogs/fragments/beats_output_hosts.yml @@ -14,3 +14,5 @@ trivial: - The three Beats templates read the resolved host lists instead of working the hosts out themselves, which removes nine inventory lookups and the ``elasticstack_full_stack`` branch around each of them. + - CONTRIBUTING.md describes which tags a task gets, so that every tag listed in a role README + works on its own.