diff --git a/changelogs/fragments/address_mode.yml b/changelogs/fragments/address_mode.yml
new file mode 100644
index 00000000..21cb6a7f
--- /dev/null
+++ b/changelogs/fragments/address_mode.yml
@@ -0,0 +1,17 @@
+minor_changes:
+ - The new ``elasticstack_address_mode`` selects how the Ansible roles address a host of the
+ elasticsearch, logstash or kibana inventory group. ``inventory`` keeps the name from the
+ inventory as before, ``fqdn``, ``hostname`` and ``ip`` take the address from the facts of that
+ host. Each host is addressed in the mode set for it in the inventory, in group_vars or
+ host_vars, or with ``-e``; a value in the vars of a play or a role parameter stops the run
+ with an error. Facts of those hosts that are missing, for example in a run limited with
+ ``--limit``, are gathered first.
+ - Setting ``elasticstack_node_address`` in the host_vars of a host makes the Ansible roles reach that
+ host at this address, whatever ``elasticstack_address_mode`` says, for example through the
+ IP of a particular network card. The address is also added to the certificate of an
+ Elasticsearch host and to the certificate a Logstash host presents to the Beats. Existing
+ certificates get it when they are renewed.
+
+trivial:
+ - The full stack Molecule scenario sets ``elasticstack_address_mode`` to ``ip`` in its inventory
+ and no longer writes the container addresses into /etc/hosts.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 324ce3f8..91ed6795 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -10,7 +10,7 @@ Hosts in these groups get the respective services. Restricting your plays to onl
Make sure every host that should be configured is part of your playbook, including hosts that only run Beats. The collection first gathers facts from all hosts and then uses facts such as host names and IP addresses to connect the tools to each other.
-You will want reliable DNS resolution, or all hosts of the stack in your systems' hosts files.
+By default Beats, Kibana and Logstash reach the other hosts by their names from your inventory, so these names have to resolve on those hosts, through DNS or the hosts files. Without that, set `elasticstack_address_mode` to `ip`, described with the [elasticstack role](../roles/elasticstack/README.md). Unlike the variables in the example playbook below, it belongs into your inventory, for example `group_vars/all`; in the `vars` of a play it stops the run.
## Before the first run
diff --git a/molecule/elasticstack_default/molecule.yml b/molecule/elasticstack_default/molecule.yml
index 1efd1575..bd120dc7 100644
--- a/molecule/elasticstack_default/molecule.yml
+++ b/molecule/elasticstack_default/molecule.yml
@@ -40,6 +40,11 @@ provisioner:
env:
ANSIBLE_ROLES_PATH: $MOLECULE_EPHEMERAL_DIRECTORY/roles:${HOME}/.ansible/roles:$MOLECULE_PROJECT_DIRECTORY/roles
inventory:
+ group_vars:
+ all:
+ # The containers cannot resolve each other's names on the GitHub runner. The roles read
+ # elasticstack_address_mode from the inventory, the vars of a play would stop the run.
+ elasticstack_address_mode: ip
host_vars:
elasticstack${ELASTIC_RELEASE}-cluster2-${MOLECULE_DISTRO}:
elasticsearch_tls_key_passphrase: UniqueHostPassword
diff --git a/molecule/elasticstack_default/prepare.yml b/molecule/elasticstack_default/prepare.yml
index 37ea6841..611cf912 100644
--- a/molecule/elasticstack_default/prepare.yml
+++ b/molecule/elasticstack_default/prepare.yml
@@ -59,20 +59,3 @@
state: latest
update_cache: yes
when: ansible_os_family == "Debian"
-
- # Temporary CI workaround: containers cannot resolve each other by name
- # (the runner's resolver is Azure's 168.63.129.16, which does not know the
- # container hostnames). Seed /etc/hosts from gathered facts so name-based
- # connections work. Remove once #524 (elasticstack_address_mode) lands and
- # molecule sets elasticstack_address_mode: ip.
- - name: Seed /etc/hosts from facts for inter-container name resolution
- ansible.builtin.blockinfile:
- path: /etc/hosts
- marker: "# {mark} ANSIBLE CLUSTER HOSTS"
- # /etc/hosts is a Docker bind-mount, so the default atomic temp+rename
- # write fails with EBUSY; write in place instead.
- unsafe_writes: true
- block: |-
- {% for h in groups['all'] %}
- {{ hostvars[h].ansible_default_ipv4.address | default(hostvars[h].ansible_all_ipv4_addresses[0]) }} {{ hostvars[h].ansible_fqdn }} {{ hostvars[h].ansible_hostname }} {{ h }}
- {% endfor %}
diff --git a/roles/beats/README.md b/roles/beats/README.md
index 06f945a2..0fe7234d 100644
--- a/roles/beats/README.md
+++ b/roles/beats/README.md
@@ -39,8 +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_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_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 addresses of the hosts in the inventory group named in elasticstack_elasticsearch_group_name, picked by elasticstack_address_mode and elasticstack_node_address. 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 addresses of the hosts in the inventory group named in elasticstack_logstash_group_name, picked by elasticstack_address_mode and elasticstack_node_address. 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/meta/argument_specs.yml b/roles/beats/meta/argument_specs.yml
index 411f5d18..f6c11b4e 100644
--- a/roles/beats/meta/argument_specs.yml
+++ b/roles/beats/meta/argument_specs.yml
@@ -45,20 +45,22 @@ argument_specs:
elements: str
description: >-
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.
+ this variable is not set, the role fills it with the addresses of the hosts in the
+ inventory group named in elasticstack_elasticsearch_group_name, picked by
+ elasticstack_address_mode and elasticstack_node_address. 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. 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.
+ variable is not set, the role fills it with the addresses of the hosts in the
+ inventory group named in elasticstack_logstash_group_name, picked by
+ elasticstack_address_mode and elasticstack_node_address. 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 3f5c5717..7c1f636f 100644
--- a/roles/beats/tasks/main.yml
+++ b/roles/beats/tasks/main.yml
@@ -3,7 +3,6 @@
- name: Include global role
ansible.builtin.import_role:
name: netways.elasticstack.elasticstack
- when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false)
- name: Update apt cache.
ansible.builtin.apt:
@@ -69,6 +68,7 @@
ansible.builtin.set_fact:
beats_elasticsearch_hosts: >-
{{ groups[elasticstack_elasticsearch_group_name] | default([])
+ | map('extract', __elasticstack_node_address) | list
if elasticstack_full_stack | bool else [] }}
when: beats_elasticsearch_hosts is undefined
tags: always
@@ -77,6 +77,7 @@
ansible.builtin.set_fact:
beats_logstash_hosts: >-
{{ groups[elasticstack_logstash_group_name] | default([])
+ | map('extract', __elasticstack_node_address) | list
if elasticstack_full_stack | bool else [] }}
when: beats_logstash_hosts is undefined
tags: always
diff --git a/roles/beats/templates/auditbeat.yml.j2 b/roles/beats/templates/auditbeat.yml.j2
index b2a688f1..237f3003 100644
--- a/roles/beats/templates/auditbeat.yml.j2
+++ b/roles/beats/templates/auditbeat.yml.j2
@@ -32,7 +32,7 @@ setup.template.settings:
index.number_of_shards: 1
setup.kibana:
{% if elasticstack_full_stack | bool and groups[elasticstack_kibana_group_name] | default([]) | length > 0 %}
- host: "http://{{ groups[elasticstack_kibana_group_name] | first }}:{{ elasticstack_kibana_port }}"
+ host: "http://{{ __elasticstack_node_address[groups[elasticstack_kibana_group_name] | first] }}:{{ elasticstack_kibana_port }}"
{% if beats_security | bool %}
username: "elastic"
password: "{{ beats_writer_password.stdout }}"
diff --git a/roles/beats/templates/filebeat.yml.j2 b/roles/beats/templates/filebeat.yml.j2
index 3d7e00bb..d34a62e0 100644
--- a/roles/beats/templates/filebeat.yml.j2
+++ b/roles/beats/templates/filebeat.yml.j2
@@ -116,7 +116,7 @@ setup.template.settings:
index.number_of_shards: 1
setup.kibana:
{% if elasticstack_full_stack | bool and groups[elasticstack_kibana_group_name] | default([]) | length > 0 %}
- host: "http://{{ groups[elasticstack_kibana_group_name] | first }}:{{ elasticstack_kibana_port }}"
+ host: "http://{{ __elasticstack_node_address[groups[elasticstack_kibana_group_name] | first] }}:{{ elasticstack_kibana_port }}"
{% if beats_security | bool %}
username: "elastic"
password: "{{ beats_writer_password.stdout }}"
diff --git a/roles/beats/templates/metricbeat.yml.j2 b/roles/beats/templates/metricbeat.yml.j2
index 373146ab..52ff5fcc 100644
--- a/roles/beats/templates/metricbeat.yml.j2
+++ b/roles/beats/templates/metricbeat.yml.j2
@@ -7,7 +7,7 @@ setup.template.settings:
index.number_of_shards: 1
setup.kibana:
{% if elasticstack_full_stack | bool and groups[elasticstack_kibana_group_name] | default([]) | length > 0 %}
- host: "http://{{ groups[elasticstack_kibana_group_name] | first }}:{{ elasticstack_kibana_port }}"
+ host: "http://{{ __elasticstack_node_address[groups[elasticstack_kibana_group_name] | first] }}:{{ elasticstack_kibana_port }}"
{% if beats_security | bool %}
username: "elastic"
password: "{{ beats_writer_password.stdout }}"
diff --git a/roles/elasticsearch/tasks/elasticsearch-security.yml b/roles/elasticsearch/tasks/elasticsearch-security.yml
index 9b71a36a..8083c998 100644
--- a/roles/elasticsearch/tasks/elasticsearch-security.yml
+++ b/roles/elasticsearch/tasks/elasticsearch-security.yml
@@ -263,9 +263,16 @@
- --name
- "{{ hostvars[item].ansible_hostname }}"
- --ip
- - "{{ hostvars[item].ansible_default_ipv4.address | default(hostvars[item].ansible_all_ipv4_addresses[0]) }}"
+ - >-
+ {{ ([hostvars[item].ansible_default_ipv4.address | default(hostvars[item].ansible_all_ipv4_addresses[0])]
+ + ([hostvars[item].elasticstack_node_address | default('')] | select('match', '^[0-9.]+$') | list))
+ | join(',') }}
- --dns
- - "{{ hostvars[item].ansible_hostname }},{{ hostvars[item].ansible_fqdn }},{{ hostvars[item].inventory_hostname }}"
+ - >-
+ {{ ([hostvars[item].ansible_hostname, hostvars[item].ansible_fqdn, hostvars[item].inventory_hostname]
+ + ([hostvars[item].elasticstack_node_address | default('')]
+ | reject('match', '^[0-9.]+$') | reject('equalto', '') | list))
+ | join(',') }}
- --pass
- "{{ (hostvars[item].elasticsearch_tls_key_passphrase is defined) | ternary(hostvars[item].elasticsearch_tls_key_passphrase, elasticsearch_tls_key_passphrase) }}"
- --out
diff --git a/roles/elasticsearch/tasks/main.yml b/roles/elasticsearch/tasks/main.yml
index 00eb880b..abe7c405 100644
--- a/roles/elasticsearch/tasks/main.yml
+++ b/roles/elasticsearch/tasks/main.yml
@@ -10,7 +10,6 @@
- name: Include global role
ansible.builtin.import_role:
name: netways.elasticstack.elasticstack
- when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false)
- name: Update apt cache.
ansible.builtin.apt:
diff --git a/roles/elasticstack/README.md b/roles/elasticstack/README.md
index 1b27c92a..f28afc69 100644
--- a/roles/elasticstack/README.md
+++ b/roles/elasticstack/README.md
@@ -35,6 +35,8 @@ The role runs automatically the first time any component role is applied to a ho
| `elasticstack_elasticsearch_group_name`
**Type**: `str`
**Default**: `"elasticsearch"` | Inventory group that holds the Elasticsearch hosts. |
| `elasticstack_logstash_group_name`
**Type**: `str`
**Default**: `"logstash"` | Inventory group that holds the Logstash hosts. |
| `elasticstack_kibana_group_name`
**Type**: `str`
**Default**: `"kibana"` | Inventory group that holds the Kibana hosts. |
+| `elasticstack_address_mode`
**Type**: `str`
**Default**: `"inventory"`
**Choices**: `inventory`, `fqdn`, `hostname`, `ip` | How the Ansible roles of this collection address a host of the elasticsearch, logstash or kibana inventory group when they configure the connections to it. "inventory" uses the host's name in the inventory, "fqdn" its fully qualified domain name, "hostname" its short host name and "ip" the IPv4 address of its network card with the default route; the last three come from the facts of that host. Each host is addressed in the mode set for it, so a value in the group_vars or host_vars of some hosts changes only how those hosts are addressed. The Ansible roles read the value from the inventory or from extra vars; a value in the vars of a play or a role parameter stops the run with an error. The certificates the collection creates contain the IP, the short name, the FQDN and the inventory name, so every mode passes the TLS hostname check. Host lists you set yourself, such as beats_elasticsearch_hosts, are used as they are. The setting does not change discovery.seed_hosts, through which the Elasticsearch nodes find each other. |
+| `elasticstack_node_address`
**Type**: `str` | Address the Ansible roles of this collection use for this host when they configure the connections to it, an IPv4 address or a DNS name that resolves to this host. Set it in the host_vars of that host. It takes precedence over elasticstack_address_mode, for example to reach a host with several network cards through the IP of a particular one. It is also added to the certificate of an Elasticsearch host and to the certificate a Logstash host presents to the Beats. A certificate that already exists gets it only when it is renewed, with the tag renew_es_cert or renew_logstash_cert. It does not change discovery.seed_hosts. |
| `elasticstack_elasticsearch_http_port`
**Type**: `int`
**Default**: `9200` | Elasticsearch HTTP API port. |
| `elasticstack_kibana_host`
**Type**: `str`
**Default**: `"{{ ansible_fqdn }}"` | Host name Kibana publishes itself under (server.publicBaseUrl). Defaults to the fully qualified domain name of the Kibana host. Set it when users reach Kibana under a different name, for example behind a reverse proxy or a load balancer. |
| `elasticstack_kibana_port`
**Type**: `int`
**Default**: `5601` | Kibana HTTP port (also used to build the public base URL). |
diff --git a/roles/elasticstack/defaults/main.yml b/roles/elasticstack/defaults/main.yml
index 21219ca1..6397562c 100644
--- a/roles/elasticstack/defaults/main.yml
+++ b/roles/elasticstack/defaults/main.yml
@@ -3,6 +3,7 @@
elasticstack_elasticsearch_group_name: elasticsearch
elasticstack_logstash_group_name: logstash
elasticstack_kibana_group_name: kibana
+elasticstack_address_mode: inventory
elasticstack_beats_port: 5044
elasticstack_ca_host: "{{ groups[elasticstack_elasticsearch_group_name][0] | default('') }}"
diff --git a/roles/elasticstack/meta/argument_specs.yml b/roles/elasticstack/meta/argument_specs.yml
index e7d26978..e29e8390 100644
--- a/roles/elasticstack/meta/argument_specs.yml
+++ b/roles/elasticstack/meta/argument_specs.yml
@@ -81,6 +81,41 @@ argument_specs:
default: kibana
description: Inventory group that holds the Kibana hosts.
+ elasticstack_address_mode:
+ type: str
+ default: inventory
+ choices:
+ - inventory
+ - fqdn
+ - hostname
+ - ip
+ description: >-
+ How the Ansible roles of this collection address a host of the elasticsearch,
+ logstash or kibana inventory group when they configure the connections to it.
+ "inventory" uses the host's name in the inventory, "fqdn" its fully qualified
+ domain name, "hostname" its short host name and "ip" the IPv4 address of its
+ network card with the default route; the last three come from the facts of that
+ host. Each host is addressed in the mode set for it, so a value in the group_vars
+ or host_vars of some hosts changes only how those hosts are addressed. The Ansible
+ roles read the value from the inventory or from extra vars; a value in the vars of
+ a play or a role parameter stops the run with an error. The certificates the
+ collection creates contain the IP, the short name, the FQDN and the inventory
+ name, so every mode passes the TLS hostname check. Host lists you set yourself,
+ such as beats_elasticsearch_hosts, are used as they are. The setting does not
+ change discovery.seed_hosts, through which the Elasticsearch nodes find each other.
+
+ elasticstack_node_address:
+ type: str
+ description: >-
+ Address the Ansible roles of this collection use for this host when they configure
+ the connections to it, an IPv4 address or a DNS name that resolves to this host.
+ Set it in the host_vars of that host. It takes precedence over
+ elasticstack_address_mode, for example to reach a host with several network cards
+ through the IP of a particular one. It is also added to the certificate of an
+ Elasticsearch host and to the certificate a Logstash host presents to the Beats. A
+ certificate that already exists gets it only when it is renewed, with the tag
+ renew_es_cert or renew_logstash_cert. It does not change discovery.seed_hosts.
+
# ----- Ports -----
elasticstack_elasticsearch_http_port:
type: int
diff --git a/roles/elasticstack/tasks/main.yml b/roles/elasticstack/tasks/main.yml
index bd92d8b9..02935de6 100644
--- a/roles/elasticstack/tasks/main.yml
+++ b/roles/elasticstack/tasks/main.yml
@@ -1,4 +1,20 @@
---
+# The other hosts read this host's elasticstack_address_mode through hostvars, which holds neither
+# play vars nor role parameters. Stop instead of silently ignoring a value set there. Outside the
+# block below, so it runs on every start of a role and not only once per host; the roles therefore
+# import this role without a when.
+- name: Check that elasticstack_address_mode comes from the inventory
+ ansible.builtin.assert:
+ that:
+ # 'inventory' is the default from defaults/main.yml, hostvars does not see role defaults
+ - elasticstack_address_mode == (hostvars[inventory_hostname].elasticstack_address_mode | default('inventory'))
+ fail_msg: >-
+ elasticstack_address_mode has to be set in the inventory (group_vars, host_vars) or with -e.
+ The other hosts read it through hostvars, which does not contain the vars of a play or role
+ parameters, so a value set there would be ignored.
+ quiet: true
+ tags: always
+
- name: Include elasticstack role
when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false) # Ensure the global meta role is only called once for each host
block:
@@ -22,6 +38,9 @@
- name: Install common packages and dependencies
ansible.builtin.import_tasks: packages.yml
+ - name: Build the addresses of the Elastic Stack hosts
+ ansible.builtin.import_tasks: node_address.yml
+
- name: Register elasticstack role has run for that host
ansible.builtin.set_fact:
_elasticstack_role_imported: true
diff --git a/roles/elasticstack/tasks/node_address.yml b/roles/elasticstack/tasks/node_address.yml
new file mode 100644
index 00000000..e0731529
--- /dev/null
+++ b/roles/elasticstack/tasks/node_address.yml
@@ -0,0 +1,48 @@
+---
+- name: Build the address of every Elastic Stack host
+ vars:
+ __elasticstack_peer_hosts: >-
+ {{ (groups[elasticstack_elasticsearch_group_name] | default([])
+ + groups[elasticstack_logstash_group_name] | default([])
+ + groups[elasticstack_kibana_group_name] | default([])) | unique }}
+ # The mode set for the host that is addressed (item), not for the host running the task.
+ # hostvars has no role defaults, so 'inventory' repeats the default from defaults/main.yml.
+ __elasticstack_peer_mode: "{{ hostvars[item].elasticstack_address_mode | default('inventory') }}"
+ tags: always
+ block:
+ # The argument specs check the mode only on the hosts of this run. Hosts left out with --limit
+ # are checked here; a misspelled mode such as "fdqn" would otherwise reach the last branch of
+ # "Pick the address" below and be used like "ip".
+ - name: Check the elasticstack_address_mode of every Elastic Stack host
+ ansible.builtin.assert:
+ that:
+ - __elasticstack_peer_mode in ['inventory', 'fqdn', 'hostname', 'ip']
+ fail_msg: "elasticstack_address_mode of {{ item }} must be inventory, fqdn, hostname or ip."
+ quiet: true
+ loop: "{{ __elasticstack_peer_hosts }}"
+
+ - name: Gather the facts of Elastic Stack hosts that are still missing
+ ansible.builtin.setup:
+ gather_subset:
+ - min
+ - network
+ delegate_to: "{{ item }}"
+ delegate_facts: true
+ loop: "{{ __elasticstack_peer_hosts }}"
+ when:
+ - hostvars[item].elasticstack_node_address is undefined
+ - __elasticstack_peer_mode != 'inventory'
+ - hostvars[item].ansible_fqdn is undefined
+
+ - name: Pick the address of every Elastic Stack host
+ ansible.builtin.set_fact:
+ __elasticstack_node_address: >-
+ {{ __elasticstack_node_address | default({}) | combine({item: (
+ hostvars[item].elasticstack_node_address
+ if hostvars[item].elasticstack_node_address is defined else
+ item if __elasticstack_peer_mode == 'inventory' else
+ hostvars[item].ansible_fqdn if __elasticstack_peer_mode == 'fqdn' else
+ hostvars[item].ansible_hostname if __elasticstack_peer_mode == 'hostname' else
+ hostvars[item].ansible_default_ipv4.address
+ | default(hostvars[item].ansible_all_ipv4_addresses[0]))}) }}
+ loop: "{{ __elasticstack_peer_hosts }}"
diff --git a/roles/kibana/README.md b/roles/kibana/README.md
index fe02e630..ab4a9b88 100644
--- a/roles/kibana/README.md
+++ b/roles/kibana/README.md
@@ -54,7 +54,7 @@ Run only parts of the role with `--tags`:
| `kibana_enable`
**Type**: `bool`
**Default**: `true` | Start and enable the Kibana service. |
| `kibana_config_backup`
**Type**: `bool`
**Default**: `true` | Keep a backup of kibana.yml whenever the role changes it. |
| `kibana_manage_yaml`
**Type**: `bool`
**Default**: `true` | Manage and overwrite kibana.yml. |
-| `kibana_elasticsearch_hosts`
**Type**: `list` of `str` | Elasticsearch hosts Kibana connects to. Defaults to the nodes from the elasticsearch group, or localhost when used standalone. |
+| `kibana_elasticsearch_hosts`
**Type**: `list` of `str` | Elasticsearch hosts Kibana connects to. When it is not set, the role fills it with the addresses of the hosts in the inventory group named in elasticstack_elasticsearch_group_name, picked by elasticstack_address_mode and elasticstack_node_address, or with localhost if that group does not exist. |
| `kibana_security`
**Type**: `bool`
**Default**: `true` | Enable X-Pack security: connect to Elasticsearch over HTTPS with authentication and set up the encryption keys. Only effective in a full stack setup with the elastic variant. |
| `kibana_sniff_on_start`
**Type**: `bool`
**Default**: `false` | Ask Elasticsearch for the list of nodes at startup (elasticsearch.sniffOnStart). |
| `kibana_sniff_on_connection_fault`
**Type**: `bool`
**Default**: `false` | Update the list of Elasticsearch nodes on connection fault (elasticsearch.sniffOnConnectionFault). |
diff --git a/roles/kibana/meta/argument_specs.yml b/roles/kibana/meta/argument_specs.yml
index 43f88202..6c721019 100644
--- a/roles/kibana/meta/argument_specs.yml
+++ b/roles/kibana/meta/argument_specs.yml
@@ -41,8 +41,10 @@ argument_specs:
type: list
elements: str
description: >-
- Elasticsearch hosts Kibana connects to. Defaults to the nodes from the
- elasticsearch group, or localhost when used standalone.
+ Elasticsearch hosts Kibana connects to. When it is not set, the role fills it with
+ the addresses of the hosts in the inventory group named in
+ elasticstack_elasticsearch_group_name, picked by elasticstack_address_mode and
+ elasticstack_node_address, or with localhost if that group does not exist.
kibana_security:
type: bool
diff --git a/roles/kibana/tasks/main.yml b/roles/kibana/tasks/main.yml
index f178f2ee..1cb7228d 100644
--- a/roles/kibana/tasks/main.yml
+++ b/roles/kibana/tasks/main.yml
@@ -10,7 +10,6 @@
- name: Include global role
ansible.builtin.import_role:
name: netways.elasticstack.elasticstack
- when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false)
- name: Update apt cache.
ansible.builtin.apt:
@@ -27,7 +26,7 @@
- name: Set Elasticsearch hosts if used with other roles
ansible.builtin.set_fact:
- kibana_elasticsearch_hosts: "{{ groups[elasticstack_elasticsearch_group_name] }}"
+ kibana_elasticsearch_hosts: "{{ groups[elasticstack_elasticsearch_group_name] | map('extract', __elasticstack_node_address) | list }}"
when:
- kibana_elasticsearch_hosts is undefined
- groups[elasticstack_elasticsearch_group_name] is defined
diff --git a/roles/logstash/README.md b/roles/logstash/README.md
index ace92725..822a838a 100644
--- a/roles/logstash/README.md
+++ b/roles/logstash/README.md
@@ -94,7 +94,7 @@ Run only parts of the role with `--tags`:
| `logstash_redis_ssl_certificate_authorities`
**Type**: `list` of `str` | List of CA certificate paths used to verify the Redis server on the Redis outputs. Rendered only when logstash_redis_tls is true. |
| `logstash_redis_ssl_supported_protocols`
**Type**: `str` | TLS protocol version accepted on the Redis outputs (for example "TLSv1.3"). Rendered only when logstash_redis_tls is true. |
| `logstash_redis_ssl_verification_mode`
**Type**: `str` | Certificate verification mode for the Redis outputs ("full" or "none"). Rendered only when logstash_redis_tls is true. |
-| `logstash_elasticsearch`
**Type**: `list` of `str` | Elasticsearch hosts for the default output. Defaults to the nodes from the elasticsearch group, or localhost when used standalone. |
+| `logstash_elasticsearch`
**Type**: `list` of `str` | Elasticsearch hosts for the default output. When it is not set and elasticstack_full_stack is true, the role fills it with the addresses of the hosts in the inventory group named in elasticstack_elasticsearch_group_name, picked by elasticstack_address_mode and elasticstack_node_address. Without that group it defaults to localhost. |
| `logstash_validate_after_inactivity`
**Type**: `str`
**Default**: `"300"` | Seconds Logstash waits before validating a previously idle connection to Elasticsearch. |
| `logstash_sniffing`
**Type**: `bool`
**Default**: `false` | Enable sniffing for additional Elasticsearch nodes. |
| `logstash_sniffing_delay`
**Type**: `str` | Seconds to wait between sniffing attempts. Unset by default. |
diff --git a/roles/logstash/meta/argument_specs.yml b/roles/logstash/meta/argument_specs.yml
index eb2bbbed..2cb09859 100644
--- a/roles/logstash/meta/argument_specs.yml
+++ b/roles/logstash/meta/argument_specs.yml
@@ -311,8 +311,11 @@ argument_specs:
type: list
elements: str
description: >-
- Elasticsearch hosts for the default output. Defaults to the nodes from
- the elasticsearch group, or localhost when used standalone.
+ Elasticsearch hosts for the default output. When it is not set and
+ elasticstack_full_stack is true, the role fills it with the addresses of the hosts
+ in the inventory group named in elasticstack_elasticsearch_group_name, picked by
+ elasticstack_address_mode and elasticstack_node_address. Without that group it
+ defaults to localhost.
logstash_validate_after_inactivity:
type: str
diff --git a/roles/logstash/tasks/logstash-security.yml b/roles/logstash/tasks/logstash-security.yml
index 31a42f4c..d3c17fe0 100644
--- a/roles/logstash/tasks/logstash-security.yml
+++ b/roles/logstash/tasks/logstash-security.yml
@@ -248,9 +248,16 @@
- --name
- "{{ ansible_hostname }}"
- --ip
- - "{{ ansible_default_ipv4.address }}"
+ - >-
+ {{ ([ansible_default_ipv4.address]
+ + ([elasticstack_node_address | default('')] | select('match', '^[0-9.]+$') | list))
+ | join(',') }}
- --dns
- - "{{ ansible_hostname }},{{ ansible_fqdn }},{{ inventory_hostname }}"
+ - >-
+ {{ ([ansible_hostname, ansible_fqdn, inventory_hostname]
+ + ([elasticstack_node_address | default('')]
+ | reject('match', '^[0-9.]+$') | reject('equalto', '') | list))
+ | join(',') }}
- --pass
- "{{ logstash_tls_key_passphrase }}"
- --pem
diff --git a/roles/logstash/tasks/main.yml b/roles/logstash/tasks/main.yml
index dbf121a1..1ff49870 100644
--- a/roles/logstash/tasks/main.yml
+++ b/roles/logstash/tasks/main.yml
@@ -3,7 +3,6 @@
- name: Include global role
ansible.builtin.import_role:
name: netways.elasticstack.elasticstack
- when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false)
- name: Update apt cache.
ansible.builtin.apt:
@@ -19,7 +18,7 @@
- name: Set Elasticsearch hosts if used with other roles
ansible.builtin.set_fact:
- logstash_elasticsearch: "{{ groups[elasticstack_elasticsearch_group_name] }}"
+ logstash_elasticsearch: "{{ groups[elasticstack_elasticsearch_group_name] | map('extract', __elasticstack_node_address) | list }}"
when:
- logstash_elasticsearch is undefined
- groups[elasticstack_elasticsearch_group_name] is defined
diff --git a/roles/repos/tasks/main.yml b/roles/repos/tasks/main.yml
index d3390966..bb37db9d 100644
--- a/roles/repos/tasks/main.yml
+++ b/roles/repos/tasks/main.yml
@@ -3,7 +3,6 @@
- name: Include global role
ansible.builtin.import_role:
name: netways.elasticstack.elasticstack
- when: not hostvars[inventory_hostname]._elasticstack_role_imported | default(false)
- name: Import RedHat tasks
ansible.builtin.import_tasks: redhat.yml