diff --git a/NOTICE.md b/NOTICE.md index 7d43b66e..e8b18e4c 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -4,4 +4,4 @@ Here's a list of sponsors who contributed by having the collection improved via outsourcing to NETWAYS. -* CID GmbH : Thank you so much for sponsoring. Especially the feature to have different types of Elasticsearch nodes in the cluster. +* CID GmbH : Thank you so much for sponsoring. Especially the feature to have different types of Elasticsearch nodes in the cluster and the ingetration of rolling upgrades. diff --git a/README.md b/README.md index dd151086..21be353c 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,13 @@ You will want to have reliable DNS resolution or enter all hosts of the stack in The variable `elasticstack_no_log` can be set to `false` if you want to see the output of all tasks. It defaults to `true` because some tasks could reveal passwords in production. -### Versioning +### Versions and upgrades -*elasticstack_version*: Version number of tools to install. Only set if you don't want the latest. (default: none). +*elasticstack_version*: Version number of tools to install. Only set if you don't want the latest on new setups. (default: none). If you already have an installation of Elastic Stack, this collection will query the version of Elasticsearch on the CA host and use it for all further installations in the same setup. (Only if you run the `elasticsearch` role before all others) Example: `7.17.2` -*elasticstack_release*: Major release version of Elastic stack to configure. (default: `7`) +*elasticstack_release*: Major release version of Elastic stack to configure. (default: `7`) Make sure it corresponds to `elasticstack_version` if you set both. -For OSS version see `elasticstack_variant` below. **IMPORTANT** Do not change the version once you have set up the stack. There are unpredictable effects to be expected when using this for upgrades. And upgrade mechanism is already on it's way. (default: none. Example: `7.17.2`) +For OSS version see `elasticstack_variant` below. *elasticstack_variant*: Variant of the stack to install. Valid values: `elastic` or `oss`. (default: `elastic`) @@ -99,6 +99,14 @@ roles: elasticstack_version: 8.8.1 ``` +#### Upgrades #### + +Set `elasticstack_version` to the version you want to upgrade to. Positively do read and understand Elastics changelog and "breaking changes" of your target version and all between your current and the target version. Do not use unless you have a valid backup. + +If an upgrade fails, you can try re-running the collection with the same settings. There are several tasks that can provide "self-healing". Please do not rely on these mechanisms, they are more of a "convenience recovery" for easier steps. + +The collection will make sure to upgrade Elasticsearch nodes one by one. + ### Default Passwords Default passwords can be seen during generation, or found later in `/usr/share/elasticsearch/initial_passwords` diff --git a/docs/role-elasticsearch.md b/docs/role-elasticsearch.md index 4099322c..37f642e4 100644 --- a/docs/role-elasticsearch.md +++ b/docs/role-elasticsearch.md @@ -13,6 +13,7 @@ Role Variables -------------- * *elasticsearch_node_types*: List of types of this very node. Please refer to [official docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html) for details. (default: not set. allowed value: array of types) ++ *elasticsearch_nodename*': Node name of the Elasticsearch node. (default: value of `ansible_hostname`) * *elasticsearch_clustername*: Name the Elasticsearch Cluster (default: `elasticsearch`) * *elasticsearch_heap*: Heapsize for Elasticsearch. (Half of free memory on host. Maximum 30GB. (default: Half of hosts memory. Min 1GB, Max 30GB) * *elasticsearch_tls_key_passphrase*: Passphrase for elasticsearch certificates (default: `PleaseChangeMeIndividually`) @@ -53,6 +54,10 @@ This variable activates a workaround to start on systems that have certain harde * *elasticsearch_seed_hosts*: Set elasticsearch seed hosts * *elasticsearch_security_enrollment*: Controls enrollment (of nodes and Kibana) to a local node that’s been autoconfigured for security. +The following variable was only integrated to speed up upgrades of non-production clusters. Use with caution and at your own risk: + +* *elasticsearch_unsafe_upgrade_restart*: This will still perform rolling upgrades, but will first update the package and then restart the service. In contrast the default behaviour is to stop the service, do the upgrade and then start again. (default: `false`) + These variables are identical over all our elastic related roles, hence the different naming schemes. * *elasticstack_ca*: Set to the inventory hostname of the host that should house the CA for certificates for inter-node communication. (default: First node in the `elasticsearch` host group) diff --git a/molecule/elasticstack_default/verify.yml b/molecule/elasticstack_default/verify.yml index 7c2b570b..775790c3 100644 --- a/molecule/elasticstack_default/verify.yml +++ b/molecule/elasticstack_default/verify.yml @@ -136,4 +136,3 @@ success_msg: "'{{ item }}' was found in nodes.content" with_inventory_hostnames: all when: groups[elasticstack_elasticsearch_group_name] | length > 1 - diff --git a/roles/beats/defaults/main.yml b/roles/beats/defaults/main.yml index 490eb610..b52689f7 100644 --- a/roles/beats/defaults/main.yml +++ b/roles/beats/defaults/main.yml @@ -6,7 +6,6 @@ beats_auditbeat: false beats_metricbeat: false beats_target_hosts: - localhost -elasticstack_beats_port: 5044 beats_logging: file beats_logpath: /var/log/beats beats_loglevel: info @@ -58,23 +57,6 @@ beats_metricbeat_modules: - system beats_metricbeat_loadbalance: true -elasticstack_release: 8 -elasticstack_full_stack: true -elasticstack_variant: elastic -elasticstack_security: true - -elasticstack_elasticsearch_group_name: elasticsearch -elasticstack_logstash_group_name: logstash - -elasticstack_ca_dir: /opt/es-ca -elasticstack_ca_pass: PleaseChangeMe -elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords -elasticstack_elasticsearch_http_port: 9200 -elasticstack_no_log: true beats_cert_validity_period: 1095 beats_cert_expiration_buffer: "+30d" beats_cert_will_expire_soon: false - -# Variables for debugging and development - -elasticstack_override_beats_tls: false diff --git a/roles/beats/tasks/auditbeat.yml b/roles/beats/tasks/auditbeat.yml index f8a0a16a..79096455 100644 --- a/roles/beats/tasks/auditbeat.yml +++ b/roles/beats/tasks/auditbeat.yml @@ -16,6 +16,8 @@ name: "{{ beats_auditbeat_package }}" enablerepo: - 'elastic-{{ elasticstack_release }}.x' + notify: + - Restart Auditbeat when: - ansible_os_family == "RedHat" - elasticstack_full_stack | bool @@ -23,6 +25,8 @@ - name: Install Auditbeat - rpm - standalone ansible.builtin.package: name: "{{ beats_auditbeat_package }}" + notify: + - Restart Auditbeat when: - ansible_os_family == "RedHat" - not elasticstack_full_stack | bool @@ -30,6 +34,8 @@ - name: Install Auditbeat - deb ansible.builtin.package: name: "{{ beats_auditbeat_package }}" + notify: + - Restart Auditbeat when: - ansible_os_family == "Debian" diff --git a/roles/beats/tasks/beats-security.yml b/roles/beats/tasks/beats-security.yml index ef034ee3..0352fd73 100644 --- a/roles/beats/tasks/beats-security.yml +++ b/roles/beats/tasks/beats-security.yml @@ -1,17 +1,5 @@ --- -- name: Install packages for security tasks - ansible.builtin.package: - name: - - unzip - - python3-cryptography - - openssl - tags: - - certificates - - renew_ca - - renew_kibana_cert - - renew_beats_cert - - name: Ensure beats certificate exists ansible.builtin.stat: path: "/etc/beats/certs/{{ inventory_hostname }}-beats.crt" diff --git a/roles/beats/tasks/filebeat.yml b/roles/beats/tasks/filebeat.yml index 965bf1ca..4c90c9ed 100644 --- a/roles/beats/tasks/filebeat.yml +++ b/roles/beats/tasks/filebeat.yml @@ -15,6 +15,8 @@ name: "{{ beats_filebeat_package }}" enablerepo: - 'elastic-{{ elasticstack_release }}.x' + notify: + - Restart Filebeat when: - ansible_os_family == "RedHat" - elasticstack_full_stack | bool @@ -22,6 +24,8 @@ - name: Install Filebeat - rpm - standalone ansible.builtin.package: name: "{{ beats_filebeat_package }}" + notify: + - Restart Filebeat when: - ansible_os_family == "RedHat" - not elasticstack_full_stack | bool @@ -29,6 +33,8 @@ - name: Install Filebeat - deb ansible.builtin.package: name: "{{ beats_filebeat_package }}" + notify: + - Restart Filebeat when: - ansible_os_family == "Debian" diff --git a/roles/beats/tasks/main.yml b/roles/beats/tasks/main.yml index 1a5d1c6d..319c9eb8 100644 --- a/roles/beats/tasks/main.yml +++ b/roles/beats/tasks/main.yml @@ -1,10 +1,8 @@ --- -- name: Include OS specific vars - ansible.builtin.include_vars: '{{ item }}' - with_first_found: - - '{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml' - - '{{ ansible_os_family }}.yml' +- name: Include global role + ansible.builtin.import_role: + name: netways.elasticstack.elasticstack - name: Update apt cache. ansible.builtin.apt: @@ -25,18 +23,6 @@ - elasticstack_variant != "oss" - not elasticstack_override_beats_tls | bool - - name: Set elasticstack_ca variable if not already done by user - ansible.builtin.set_fact: - elasticstack_ca: "{{ groups[elasticstack_elasticsearch_group_name][0] }}" - when: - - beats_security | bool - - elasticstack_ca is undefined - - groups[elasticstack_elasticsearch_group_name] is defined - tags: - - certificates - - renew_ca - - renew_beats_cert - - name: Set beats_ca_dir if whole stack is used ansible.builtin.set_fact: beats_ca_dir: "/etc/beats/certs" diff --git a/roles/beats/tasks/metricbeat.yml b/roles/beats/tasks/metricbeat.yml index e65d6094..c261a962 100644 --- a/roles/beats/tasks/metricbeat.yml +++ b/roles/beats/tasks/metricbeat.yml @@ -16,6 +16,8 @@ name: "{{ beats_metricbeat_package }}" enablerepo: - 'elastic-{{ elasticstack_release }}.x' + notify: + - Restart Metricbeat when: - ansible_os_family == "RedHat" - elasticstack_full_stack | bool @@ -23,6 +25,8 @@ - name: Install Metricbeat - rpm - standalone ansible.builtin.package: name: "{{ beats_metricbeat_package }}" + notify: + - Restart Metricbeat when: - ansible_os_family == "RedHat" - not elasticstack_full_stack | bool @@ -30,6 +34,8 @@ - name: Install Metricbeat - deb ansible.builtin.package: name: "{{ beats_metricbeat_package }}" + notify: + - Restart Metricbeat when: - ansible_os_family == "Debian" diff --git a/roles/beats/vars/Debian.yml b/roles/beats/vars/Debian.yml deleted file mode 100644 index 1713160e..00000000 --- a/roles/beats/vars/Debian.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/default/elasticsearch -elasticstack_versionseparator: "=" diff --git a/roles/beats/vars/RedHat.yml b/roles/beats/vars/RedHat.yml deleted file mode 100644 index d12aa3b5..00000000 --- a/roles/beats/vars/RedHat.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/sysconfig/elasticsearch -elasticstack_versionseparator: "-" diff --git a/roles/beats/vars/main.yml b/roles/beats/vars/main.yml deleted file mode 100644 index ea359dc8..00000000 --- a/roles/beats/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for beats diff --git a/roles/elasticsearch/defaults/main.yml b/roles/elasticsearch/defaults/main.yml index 6f6fd505..34089ad6 100644 --- a/roles/elasticsearch/defaults/main.yml +++ b/roles/elasticsearch/defaults/main.yml @@ -31,39 +31,18 @@ elasticsearch_heap_dump_path: "/var/lib/elasticsearch" elasticsearch_jna_workaround: false -# The following variables are to be used when activating security -# They follow a different naming scheme to show that they are global -# to our set of Elastic Stack related Ansible roles - -# elasticstack_ca: First host in the `elasticsearch` group -elasticstack_ca_dir: /opt/es-ca -elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords elasticsearch_initialized_file: "{{ elasticstack_initial_passwords | dirname }}/cluster_initialized" -elasticstack_ca_name: "CN=Elastic Certificate Tool Autogenerated CA" -elasticstack_ca_pass: PleaseChangeMe -elasticstack_ca_validity_period: 1095 elasticsearch_tls_key_passphrase: PleaseChangeMeIndividually elasticsearch_cert_validity_period: 1095 -elasticstack_ca_expiration_buffer: 30 elasticsearch_cert_expiration_buffer: 30 -elasticstack_ca_will_expire_soon: false elasticsearch_cert_will_expire_soon: false elasticsearch_ssl_verification_mode: full +# use this only for non-prod environments and at your own risk! +elasticsearch_unsafe_upgrade_restart: false + # only used internally elasticsearch_freshstart: changed: false elasticsearch_freshstart_security: changed: false - -# "global" variables for all roles - -elasticstack_release: 8 -elasticstack_full_stack: true -elasticstack_variant: elastic -elasticstack_elasticsearch_http_port: 9200 -elasticstack_no_log: true - -elasticstack_elasticsearch_group_name: elasticsearch -elasticstack_logstash_group_name: logstash -elasticstack_kibana_group_name: kibana diff --git a/roles/elasticsearch/tasks/elasticsearch-rolling-upgrade.yml b/roles/elasticsearch/tasks/elasticsearch-rolling-upgrade.yml new file mode 100644 index 00000000..19801a76 --- /dev/null +++ b/roles/elasticsearch/tasks/elasticsearch-rolling-upgrade.yml @@ -0,0 +1,218 @@ +# Ansible +# +# Rolling Upgrade of Elasticsearch with security on +# Source from: author: Jeff Steinmetz, @jeffsteinmetz; Bin Li, @holysoros +# Modifications: author: Daniel Neuberger @netways.de +# More modifications: NETWAYS Professional Services GmbH +# latest tested with Ansible 2.9 and later + +--- + +# For now we support upgrade only for clusters with security enabled +# If you positively need support for safely upgrading clusters without security, +# feel free to open an issue at https://github.com/NETWAYS/ansible-collection-elasticstack/issues +- name: Set connection protocol to https + ansible.builtin.set_fact: + elasticsearch_http_protocol: "https" + +- name: Check for running Elasticsearch service + ansible.builtin.systemd: + name: elasticsearch + register: elasticsearch_running + +- name: Update stopped services right away + when: + - elasticsearch_running.status.ActiveState == "inactive" + block: + - name: Update stopped Elasticsearch - rpm with managed repositories + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + enablerepo: + - 'elastic-{% if elasticstack_variant == "oss" %}oss-{% endif %}{{ elasticstack_release }}.x' + when: + - ansible_os_family == "RedHat" + - elasticstack_full_stack | bool + + - name: Update stopped Elasticsearch - deb or unmanaged repositories rpm + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + when: + - ansible_os_family == "Debian" or + not elasticstack_full_stack | bool + +- name: Update single instances without extra caution + when: + - groups[elasticstack_elasticsearch_group_name] | length == 1 + block: + - name: Update single instances without extra caution - deb or unmanaged repositories rpm + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + when: + - ansible_os_family == "Debian" or + not elasticstack_full_stack | bool + notify: + - Restart Elasticsearch + + - name: Update single instances without extra caution - rpm with managed repositories + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + enablerepo: + - 'elastic-{% if elasticstack_variant == "oss" %}oss-{% endif %}{{ elasticstack_release }}.x' + when: + - ansible_os_family == "RedHat" + - elasticstack_full_stack | bool + notify: + - Restart Elasticsearch + + +- name: Be careful about upgrade when Elasticsearch is running + when: + - elasticsearch_running.status.ActiveState == "active" + - groups[elasticstack_elasticsearch_group_name] | length > 1 + block: + + # Usually we should not need this step. It's only there to recover from broken upgrade plays + # Without this step the cluster would never recover and the play would always fail + - name: Enable shard allocation for the cluster + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cluster/settings" + method: PUT + body: '{ "persistent": { "cluster.routing.allocation.enable": null }}' + body_format: json + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + register: response + # next line is boolean not string, so no quotes around true + # use python truthiness + until: "response.json.acknowledged == true" + retries: 5 + delay: 30 + + # this step is key!!! Don't restart more nodes + # until all shards have completed recovery + - name: Wait for cluster health to return to green + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cluster/health" + method: GET + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + register: response + until: "response.json.status == 'green'" + retries: 50 + delay: 30 + + # Disabling shard allocation right after enabling it seems redundant. Please see above for details. + - name: Disable shard allocation for the cluster + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cluster/settings" + method: PUT + body: '{ "persistent": { "cluster.routing.allocation.enable": "none" }}' + body_format: json + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + + - name: Stop non essential indexing to speed up shard recovery + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_flush" + method: POST + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + failed_when: false + + - name: Shutdown elasticsearch service + ansible.builtin.service: + name: elasticsearch + enabled: yes + state: stopped + when: + - not elasticsearch_unsafe_upgrade_restart | bool + + - name: Update Elasticsearch - rpm with managed repositories + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + enablerepo: + - 'elastic-{% if elasticstack_variant == "oss" %}oss-{% endif %}{{ elasticstack_release }}.x' + when: + - ansible_os_family == "RedHat" + - elasticstack_full_stack | bool + + - name: Update Elasticsearch - deb or unmanaged repositories rpm + ansible.builtin.package: + name: "{{ elasticsearch_package }}" + when: + - ansible_os_family == "Debian" or + not elasticstack_full_stack | bool + + - name: Start elasticsearch + ansible.builtin.service: + name: elasticsearch + enabled: yes + state: started + when: + - elasticsearch_running.status.ActiveState == "active" + - not elasticsearch_unsafe_upgrade_restart | bool + + - name: Restart elasticsearch (fast, for non-prod) + ansible.builtin.service: + name: elasticsearch + enabled: yes + state: restarted + when: + - elasticsearch_running.status.ActiveState == "active" + - elasticsearch_unsafe_upgrade_restart | bool + + - name: Wait for elasticsearch node to come back up if it was stopped + ansible.builtin.wait_for: + host: "{{ elasticsearch_api_host }}" + port: "{{ elasticstack_elasticsearch_http_port }}" + delay: 30 + + - name: Confirm the node joins the cluster # noqa: risky-shell-pipe + ansible.builtin.shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + curl + -k + -u elastic:{{ elasticstack_password.stdout }} + -s + -m 2 + '{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cat/nodes?h=name' + | grep + -E + '^{{ elasticsearch_nodename }}$' + register: result + until: result.rc == 0 + retries: 200 + delay: 3 + changed_when: false + + - name: Enable shard allocation for the cluster + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cluster/settings" + method: PUT + body: '{ "persistent": { "cluster.routing.allocation.enable": null }}' + body_format: json + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + register: response + # next line is boolean not string, so no quotes around true + # use python truthiness + until: "response.json.acknowledged == true" + retries: 5 + delay: 30 + + - name: Wait for cluster health to return to yellow or green + ansible.builtin.uri: + url: "{{ elasticsearch_http_protocol }}://{{ elasticsearch_api_host }}:{{ elasticstack_elasticsearch_http_port }}/_cluster/health" + method: GET + user: elastic + password: "{{ elasticstack_password.stdout }}" + validate_certs: no + register: response + until: "response.json.status == 'yellow' or response.json.status == 'green'" + retries: 5 + delay: 30 diff --git a/roles/elasticsearch/tasks/elasticsearch-security.yml b/roles/elasticsearch/tasks/elasticsearch-security.yml index e86e4cab..d8166921 100644 --- a/roles/elasticsearch/tasks/elasticsearch-security.yml +++ b/roles/elasticsearch/tasks/elasticsearch-security.yml @@ -1,26 +1,5 @@ --- -- name: Install packages for security tasks - ansible.builtin.package: - name: - - unzip - - python3-cryptography - - openssl - tags: - - certificates - - renew_ca - - renew_kibana_cert - - renew_es_cert - -- name: Set elasticstack_ca variable if not already done by user - ansible.builtin.set_fact: - elasticstack_ca: "{{ groups[elasticstack_elasticsearch_group_name][0] }}" - when: elasticstack_ca is undefined - tags: - - certificates - - renew_ca - - renew_es_cert - - name: Ensure ca exists ansible.builtin.stat: path: "{{ elasticstack_ca_dir }}/elastic-stack-ca.p12" diff --git a/roles/elasticsearch/tasks/main.yml b/roles/elasticsearch/tasks/main.yml index cfad7f59..7d99d877 100644 --- a/roles/elasticsearch/tasks/main.yml +++ b/roles/elasticsearch/tasks/main.yml @@ -1,5 +1,9 @@ --- +- name: Include global role + ansible.builtin.import_role: + name: netways.elasticstack.elasticstack + - name: Update apt cache. ansible.builtin.apt: update_cache: yes @@ -16,6 +20,12 @@ - '{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml' - '{{ ansible_os_family }}.yml' +- name: Set node name if not overriden by user + ansible.builtin.set_fact: + elasticsearch_nodename: "{{ ansible_hostname }}" + when: + - elasticsearch_nodename is undefined + - name: Set common password for common certificates ansible.builtin.set_fact: elasticsearch_tls_key_passphrase: "{{ elasticstack_cert_pass }}" @@ -121,6 +131,16 @@ replace(' ', '') }} +- name: Update Elasticsearch if needed + ansible.builtin.include_tasks: elasticsearch-rolling-upgrade.yml + with_items: "{{ groups[elasticstack_elasticsearch_group_name] }}" + when: + - "hostvars[item].inventory_hostname == inventory_hostname" + - elasticstack_version is defined + - ansible_facts.packages['elasticsearch'][0].version is defined + - elasticstack_password.stdout is defined + - elasticstack_version is version( ansible_facts.packages['elasticsearch'][0].version, '>') + - name: Install Elasticsearch - rpm - full stack ansible.builtin.package: name: "{{ elasticsearch_package }}" diff --git a/roles/elasticsearch/templates/elasticsearch.yml.j2 b/roles/elasticsearch/templates/elasticsearch.yml.j2 index 8382f97c..1d4238cd 100644 --- a/roles/elasticsearch/templates/elasticsearch.yml.j2 +++ b/roles/elasticsearch/templates/elasticsearch.yml.j2 @@ -1,6 +1,7 @@ +# test {{ ansible_managed | comment }} -node.name: "{{ ansible_hostname }}" +node.name: "{{ elasticsearch_nodename }}" path.data: {{ elasticsearch_datapath }} path.logs: {{ elasticsearch_logpath }} cluster.name: "{{ elasticsearch_clustername }}" @@ -44,11 +45,11 @@ discovery.seed_hosts: [ {% for host in groups[elasticstack_elasticsearch_group_n {% if not elaticsearch_cluster_set_up | bool and groups[elasticstack_elasticsearch_group_name] | length > 1 %} {% if elasticsearch_node_types is defined %} cluster.initial_master_nodes: [ {% for host in groups['elasticsearch_role_master'] %} -"{{ hostvars[host].ansible_hostname }}"{% if not loop.last %},{% endif %} +"{{ hostvars[host]['elasticsearch_nodename'] }}"{% if not loop.last %},{% endif %} {% endfor %} ] {% else %} cluster.initial_master_nodes: [ {% for host in groups[elasticstack_elasticsearch_group_name] %} -"{{ hostvars[host].ansible_hostname }}"{% if not loop.last %},{% endif %} +"{{ hostvars[host]['elasticsearch_nodename'] }}"{% if not loop.last %},{% endif %} {% endfor %} ] {% endif %} {% endif %} diff --git a/roles/elasticsearch/vars/Debian.yml b/roles/elasticsearch/vars/Debian.yml index 1713160e..bb0878c1 100644 --- a/roles/elasticsearch/vars/Debian.yml +++ b/roles/elasticsearch/vars/Debian.yml @@ -1,4 +1,3 @@ --- elasticsearch_sysconfig_file: /etc/default/elasticsearch -elasticstack_versionseparator: "=" diff --git a/roles/elasticsearch/vars/RedHat.yml b/roles/elasticsearch/vars/RedHat.yml index d12aa3b5..f0dbc02a 100644 --- a/roles/elasticsearch/vars/RedHat.yml +++ b/roles/elasticsearch/vars/RedHat.yml @@ -1,4 +1,3 @@ --- elasticsearch_sysconfig_file: /etc/sysconfig/elasticsearch -elasticstack_versionseparator: "-" diff --git a/roles/elasticstack/defaults/main.yml b/roles/elasticstack/defaults/main.yml new file mode 100644 index 00000000..bb35596e --- /dev/null +++ b/roles/elasticstack/defaults/main.yml @@ -0,0 +1,27 @@ +--- + +elasticstack_elasticsearch_group_name: elasticsearch +elasticstack_logstash_group_name: logstash +elasticstack_kibana_group_name: kibana + +elasticstack_beats_port: 5044 +elasticstack_ca_dir: /opt/es-ca +elasticstack_ca_expiration_buffer: 30 +elasticstack_ca_name: "CN=Elastic Certificate Tool Autogenerated CA" +elasticstack_ca_pass: PleaseChangeMe +elasticstack_ca_validity_period: 1095 +elasticstack_ca_will_expire_soon: false +elasticstack_elasticsearch_http_port: 9200 +elasticstack_enable_repos: true +elasticstack_full_stack: true +elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords +elasticstack_kibana_port: 5601 +elasticstack_override_beats_tls: false +elasticstack_release: 8 +elasticstack_repo_key: https://artifacts.elastic.co/GPG-KEY-elasticsearch +elasticstack_rpm_workaround: false +elasticstack_security: true +elasticstack_variant: elastic + + # for debugging only +elasticstack_no_log: true diff --git a/roles/elasticstack/tasks/elasticstack-passwords.yml b/roles/elasticstack/tasks/elasticstack-passwords.yml new file mode 100644 index 00000000..1f2bc2a1 --- /dev/null +++ b/roles/elasticstack/tasks/elasticstack-passwords.yml @@ -0,0 +1,18 @@ +--- + +- name: Check for passwords being set + ansible.builtin.stat: + path: "{{ elasticstack_initial_passwords }}" + delegate_to: "{{ elasticstack_ca }}" + register: elasticsearch_passwords_file + +- name: Fetch Elastic password # noqa: risky-shell-pipe + ansible.builtin.shell: > + if test -n "$(ps -p $$ | grep bash)"; then set -o pipefail; fi; + grep "PASSWORD elastic" {{ elasticstack_initial_passwords }} | + awk {' print $4 '} + register: elasticstack_password + changed_when: false + no_log: "{{ elasticstack_no_log }}" + delegate_to: "{{ elasticstack_ca }}" + when: elasticsearch_passwords_file.stat.exists | bool diff --git a/roles/elasticstack/tasks/elasticstack-versions.yml b/roles/elasticstack/tasks/elasticstack-versions.yml new file mode 100644 index 00000000..0f5421f8 --- /dev/null +++ b/roles/elasticstack/tasks/elasticstack-versions.yml @@ -0,0 +1,13 @@ +--- + +- name: Gather package facts + ansible.builtin.package_facts: + manager: auto + +- name: Set target version to Elasticsearch on CA host + ansible.builtin.set_fact: + elasticstack_version: "{{ ansible_facts.packages['elasticsearch'][0].version }}" + delegate_to: "{{ elasticstack_ca }}" + when: + - ansible_facts.packages['elasticsearch'][0].version is defined + - elasticstack_version is undefined diff --git a/roles/elasticstack/tasks/main.yml b/roles/elasticstack/tasks/main.yml new file mode 100644 index 00000000..e8797325 --- /dev/null +++ b/roles/elasticstack/tasks/main.yml @@ -0,0 +1,34 @@ +--- + +- name: Include OS specific vars + ansible.builtin.include_vars: '{{ item }}' + with_first_found: + - '{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml' + - '{{ ansible_os_family }}.yml' + +- name: Set elasticstack_ca variable if not already done by user + ansible.builtin.set_fact: + elasticstack_ca: "{{ groups[elasticstack_elasticsearch_group_name][0] }}" + when: + - elasticstack_ca is undefined + - groups[elasticstack_elasticsearch_group_name][0] is defined + +- name: Set elasticstack_ca variable if not already set to Elasticsearch server + ansible.builtin.set_fact: + elasticstack_ca: "{{ groups[elasticstack_logstash_group_name][0] }}" + when: + - elasticstack_ca is undefined + - groups[elasticstack_logstash_group_name][0] is defined + +- name: Set versions for components + ansible.builtin.import_tasks: elasticstack-versions.yml + +- name: Fetch passwords if passwords are initialized + ansible.builtin.import_tasks: elasticstack-passwords.yml + +- name: Set elasticstack_globals_set for other roles to skip this role + ansible.builtin.set_fact: + elasticstack_globals_set: true + +- name: Install common packages and dependencies + ansible.builtin.import_tasks: packages.yml diff --git a/roles/elasticstack/tasks/packages.yml b/roles/elasticstack/tasks/packages.yml new file mode 100644 index 00000000..36a2f3f7 --- /dev/null +++ b/roles/elasticstack/tasks/packages.yml @@ -0,0 +1,22 @@ +--- + +- name: Update apt cache. + ansible.builtin.apt: + update_cache: yes + cache_valid_time: 600 + changed_when: false + when: ansible_os_family == 'Debian' + +- name: Install packages for security tasks + ansible.builtin.package: + name: + - unzip + - python3-cryptography + - openssl + tags: + - certificates + - renew_ca + - renew_kibana_cert + - renew_beats_cert + - renew_es_cert + - renew_logstash_cert diff --git a/roles/elasticstack/vars/Debian.yml b/roles/elasticstack/vars/Debian.yml new file mode 100644 index 00000000..3d9e31b5 --- /dev/null +++ b/roles/elasticstack/vars/Debian.yml @@ -0,0 +1,3 @@ +--- + +elasticstack_versionseparator: "=" diff --git a/roles/elasticstack/vars/RedHat.yml b/roles/elasticstack/vars/RedHat.yml new file mode 100644 index 00000000..a8d601fe --- /dev/null +++ b/roles/elasticstack/vars/RedHat.yml @@ -0,0 +1,3 @@ +--- + +elasticstack_versionseparator: "-" diff --git a/roles/logstash/vars/main.yml b/roles/elasticstack/vars/main.yml similarity index 100% rename from roles/logstash/vars/main.yml rename to roles/elasticstack/vars/main.yml diff --git a/roles/kibana/defaults/main.yml b/roles/kibana/defaults/main.yml index b29b707a..725d70f5 100644 --- a/roles/kibana/defaults/main.yml +++ b/roles/kibana/defaults/main.yml @@ -9,9 +9,6 @@ kibana_tls: false kibana_tls_cert: /etc/kibana/certs/cert.pem kibana_tls_key: /etc/kibana/certs/key.pem kibana_tls_key_passphrase: PleaseChangeMe -elasticstack_ca_dir: /opt/es-ca -elasticstack_ca_pass: PleaseChangeMe -elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords kibana_cert_expiration_buffer: 30 kibana_cert_validity_period: 1095 kibana_cert_will_expire_soon: false @@ -20,13 +17,3 @@ kibana_sniff_on_connection_fault: false kibana_freshstart: changed: false - -# "global" variables for all roles -elasticstack_release: 8 -elasticstack_full_stack: true -elasticstack_variant: elastic -elasticstack_elasticsearch_http_port: 9200 -elasticstack_kibana_port: 5601 -elasticstack_no_log: true - -elasticstack_elasticsearch_group_name: elasticsearch diff --git a/roles/kibana/tasks/kibana-security.yml b/roles/kibana/tasks/kibana-security.yml index 77f53bdb..553b74c7 100644 --- a/roles/kibana/tasks/kibana-security.yml +++ b/roles/kibana/tasks/kibana-security.yml @@ -1,27 +1,5 @@ --- -- name: Install packages for security tasks - ansible.builtin.package: - name: - - unzip - - python3-cryptography - - openssl - tags: - - certificates - - renew_ca - - renew_kibana_cert - -- name: Set elasticstack_ca variable if not already done by user - ansible.builtin.set_fact: - elasticstack_ca: "{{ groups[elasticstack_elasticsearch_group_name][0] }}" - when: - - elasticstack_ca is undefined - - groups[elasticstack_elasticsearch_group_name] is defined - tags: - - certificates - - renew_ca - - renew_kibana_cert - - name: Ensure kibana certificate exists ansible.builtin.stat: path: "/etc/kibana/certs/{{ ansible_hostname }}-kibana.p12" diff --git a/roles/kibana/tasks/main.yml b/roles/kibana/tasks/main.yml index 80dba32b..c4a9c4c7 100644 --- a/roles/kibana/tasks/main.yml +++ b/roles/kibana/tasks/main.yml @@ -1,5 +1,9 @@ --- +- name: Include global role + ansible.builtin.import_role: + name: netways.elasticstack.elasticstack + - name: Update apt cache. ansible.builtin.apt: update_cache: yes @@ -7,12 +11,6 @@ changed_when: false when: ansible_os_family == 'Debian' -- name: Include OS specific vars - ansible.builtin.include_vars: '{{ item }}' - with_first_found: - - '{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml' - - '{{ ansible_os_family }}.yml' - - name: Set common password for common certificates ansible.builtin.set_fact: kibana_tls_key_passphrase: "{{ elasticstack_cert_pass }}" @@ -50,6 +48,8 @@ name: "{{ kibana_package }}" enablerepo: - 'elastic-{% if elasticstack_variant == "oss" %}oss-{% endif %}{{ elasticstack_release }}.x' + notify: + - Restart Kibana when: - ansible_os_family == "RedHat" - elasticstack_full_stack | bool @@ -57,6 +57,8 @@ - name: Install Kibana - rpm - standalone ansible.builtin.package: name: "{{ kibana_package }}" + notify: + - Restart Kibana when: - ansible_os_family == "RedHat" - not elasticstack_full_stack | bool @@ -64,6 +66,8 @@ - name: Install Kibana - deb ansible.builtin.package: name: "{{ kibana_package }}" + notify: + - Restart Kibana when: - ansible_os_family == "Debian" diff --git a/roles/kibana/vars/Debian.yml b/roles/kibana/vars/Debian.yml deleted file mode 100644 index 1713160e..00000000 --- a/roles/kibana/vars/Debian.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/default/elasticsearch -elasticstack_versionseparator: "=" diff --git a/roles/kibana/vars/RedHat.yml b/roles/kibana/vars/RedHat.yml deleted file mode 100644 index d12aa3b5..00000000 --- a/roles/kibana/vars/RedHat.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/sysconfig/elasticsearch -elasticstack_versionseparator: "-" diff --git a/roles/kibana/vars/main.yml b/roles/kibana/vars/main.yml deleted file mode 100644 index dcb62ac2..00000000 --- a/roles/kibana/vars/main.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -# vars file for kibana diff --git a/roles/logstash/defaults/main.yml b/roles/logstash/defaults/main.yml index f510d5b4..2a2a7690 100644 --- a/roles/logstash/defaults/main.yml +++ b/roles/logstash/defaults/main.yml @@ -77,22 +77,3 @@ logstash_pipeline_identifier_defaults: false logstash_freshstart: changed: false - -elasticstack_ca_dir: /opt/es-ca -elasticstack_initial_passwords: /usr/share/elasticsearch/initial_passwords -elasticstack_ca_pass: PleaseChangeMe - -# "global" variables for all roles - -elasticstack_release: 8 -elasticstack_full_stack: true -elasticstack_variant: elastic -elasticstack_security: true -elasticstack_elasticsearch_http_port: 9200 -elasticstack_beats_port: 5044 - -elasticstack_elasticsearch_group_name: elasticsearch - -# Variables for debugging and development - -elasticstack_override_beats_tls: false diff --git a/roles/logstash/tasks/logstash-security.yml b/roles/logstash/tasks/logstash-security.yml index 777f18bc..e83b6c07 100644 --- a/roles/logstash/tasks/logstash-security.yml +++ b/roles/logstash/tasks/logstash-security.yml @@ -1,29 +1,5 @@ --- -- name: Install packages for security tasks - ansible.builtin.package: - name: - - unzip - - python3-cryptography - - openssl - tags: - - certificates - - renew_ca - - renew_logstash_cert - -- name: Set elasticstack_ca variable if not already done by user - ansible.builtin.set_fact: - elasticstack_ca: "{{ groups[elasticstack_elasticsearch_group_name][0] }}" - when: - - elasticstack_ca is undefined - - groups[elasticstack_elasticsearch_group_name] is defined - tags: - - certificates - - configuration - - logstash_configuration - - renew_ca - - renew_logstash_cert - - name: Ensure logstash certificate exists ansible.builtin.stat: path: "{{ logstash_certs_dir }}/{{ ansible_hostname }}-ls.p12" diff --git a/roles/logstash/tasks/main.yml b/roles/logstash/tasks/main.yml index 39425258..00e4a70a 100644 --- a/roles/logstash/tasks/main.yml +++ b/roles/logstash/tasks/main.yml @@ -1,5 +1,9 @@ --- +- name: Include global role + ansible.builtin.import_role: + name: netways.elasticstack.elasticstack + - name: Update apt cache. ansible.builtin.apt: update_cache: yes @@ -7,12 +11,6 @@ changed_when: false when: ansible_os_family == 'Debian' -- name: Include OS specific vars - ansible.builtin.include_vars: '{{ item }}' - with_first_found: - - '{{ ansible_os_family }}_{{ ansible_distribution_major_version }}.yml' - - '{{ ansible_os_family }}.yml' - - name: Prepare for whole stack roles if used when: - elasticstack_full_stack | bool @@ -58,7 +56,7 @@ - renew_ca - renew_logstash_cert -- name: Construct exact name of Logstas package +- name: Construct exact name of Logstash package ansible.builtin.set_fact: logstash_package: > {{ @@ -69,12 +67,32 @@ string if elasticstack_version is defined else '') | replace(' ', '') }} + when: + - ansible_os_family != "Debian" + +- name: Construct exact name of Logstash package + ansible.builtin.set_fact: + logstash_package: > + {{ + 'logstash' + + ('-oss' if elasticstack_variant == 'oss' else '') + + (elasticstack_versionseparator + + '1:' + + elasticstack_version + + '-1' | + string if elasticstack_version is defined else '') | + replace(' ', '') + }} + when: + - ansible_os_family == "Debian" - name: Install Logstash - rpm - full stack ansible.builtin.package: name: "{{ logstash_package }}" enablerepo: - 'elastic-{% if elasticstack_variant == "oss" %}oss-{% endif %}{{ elasticstack_release }}.x' + notify: + - Restart Logstash when: - ansible_os_family == "RedHat" - elasticstack_full_stack | bool @@ -82,6 +100,8 @@ - name: Install Logstash - rpm - standalone ansible.builtin.package: name: "{{ logstash_package }}" + notify: + - Restart Logstash when: - ansible_os_family == "RedHat" - not elasticstack_full_stack | bool @@ -89,6 +109,8 @@ - name: Install Logstash - deb ansible.builtin.package: name: "{{ logstash_package }}" + notify: + - Restart Logstash when: - ansible_os_family == "Debian" diff --git a/roles/logstash/vars/Debian.yml b/roles/logstash/vars/Debian.yml deleted file mode 100644 index 1713160e..00000000 --- a/roles/logstash/vars/Debian.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/default/elasticsearch -elasticstack_versionseparator: "=" diff --git a/roles/logstash/vars/RedHat.yml b/roles/logstash/vars/RedHat.yml deleted file mode 100644 index d12aa3b5..00000000 --- a/roles/logstash/vars/RedHat.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -elasticsearch_sysconfig_file: /etc/sysconfig/elasticsearch -elasticstack_versionseparator: "-" diff --git a/roles/repos/defaults/main.yml b/roles/repos/defaults/main.yml deleted file mode 100644 index dae1b1b9..00000000 --- a/roles/repos/defaults/main.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# defaults file for elastic-repos -elasticstack_repo_key: https://artifacts.elastic.co/GPG-KEY-elasticsearch -elasticstack_release: 8 -elasticstack_full_stack: true -elasticstack_variant: elastic - -elasticstack_rpm_workaround: false - -elasticstack_enable_repos: true -elasticstack_no_log: true - -elasticstack_elasticsearch_group_name: elasticsearch diff --git a/roles/repos/tasks/main.yml b/roles/repos/tasks/main.yml index b78cfbcf..340b4c43 100644 --- a/roles/repos/tasks/main.yml +++ b/roles/repos/tasks/main.yml @@ -1,5 +1,9 @@ --- +- name: Include global role + ansible.builtin.import_role: + name: netways.elasticstack.elasticstack + - name: Check for versions ansible.builtin.fail: msg: "No OSS versions later than 7 are available" diff --git a/roles/repos/vars/main.yml b/roles/repos/vars/main.yml deleted file mode 100644 index ed97d539..00000000 --- a/roles/repos/vars/main.yml +++ /dev/null @@ -1 +0,0 @@ ----