diff --git a/create-kibana-objects.yml b/create-kibana-objects.yml new file mode 100644 index 0000000..5964ccd --- /dev/null +++ b/create-kibana-objects.yml @@ -0,0 +1,184 @@ +--- + +# creates elastic objetcs for smardigo instances +# to empower dudes to find relevant logmessages faster and +# reduce/abolish "monkey business" in creating needed ES-related objects for (devops|admin)-dudes +# - executed on stage specific server: {{ stage }}-elastic-stack-kibana-01-kibana + +# Parameters: +# playbook inventory +# stage := the type of the stage (e.g. dev, int, qa, prod) +# tenant_id := (unique key for the tenant, e.g. customer) +# cluster_name := (business name for the cluster, e.g. product, department ) +# cluster_size := (WIP node count for the cluster) (Currently max is 2 master/slave) +# cluster_services_str := (services to setup, e.g. 'connect,wordpress') +# smardigo message callback +# scope_id := (scope id of the management process) +# process_instance_id := (process instance id of the management process) +# smardigo_management_action := (smardigo management action anme of the management process) + +############################################################# +# Creating inventory dynamically for given parameters +############################################################# + +- hosts: localhost + gather_facts: false + connection: local + + pre_tasks: + - name: "Check if ansible version is at least 2.10.x" + assert: + that: + - ansible_version.major >= 2 + - ansible_version.minor >= 10 + msg: "The ansible version has to be at least ({{ ansible_version.full }})" + + - set_fact: + cluster_services: "{{ cluster_services_str | split(',') }}" + + tasks: + - name: Add kibana server(s) to hosts if necessary + add_host: + name: "{{ stage }}-elastic-stack-kibana-01-kibana" + groups: + - "stage_{{ stage }}" + with_items: "{{ cluster_services }}" + when: item in ['connect'] + +############################################################# +# configure elastic search objects +############################################################# +- hosts: "stage_{{ stage }}" + serial: "{{ serial_number | default(1) }}" + gather_facts: no + remote_user: root + vars: + ansible_connection: local + ansible_ssh_host: "{{ stage_server_domain }}" + api_endpoint: '{{ stage }}-elastic-stack-kibana-01-kibana.{{ domain }}' + elastic_state: present + elastic_users: + - + username: '{{ stage }}-{{ tenant_id }}' + roles: + - '{{ stage }}-{{ tenant_id }}' + full_name: '' + password: '{{ password | default( stage + "-" + tenant_id ) }}' + email: '{{ email | default("") }}' + enabled: true + elastic_state: '{{ elastic_state }}' + elastic_roles: + - + elastic_state: '{{ elastic_state }}' + name: '{{ stage }}-{{ tenant_id }}' + elasticsearch: + cluster: [] + indices: + - names: + - '{{ stage }}-{{ tenant_id }}-{{ cluster_name }}-*' + privileges: + - read + - read_cross_cluster + allow_restricted_indices: false + run_as: [] + kibana: + - base: [] + feature: + advancedSettings: + - read + dashboard: + - read + discover: + - read + indexPatterns: + - read + savedObjectsManagement: + - read + spaces: + - '{{ stage }}-{{ tenant_id }}' + elastic_spaces: + - + elastic_state: '{{ elastic_state }}' + id: &es_space_name '{{ stage }}-{{ tenant_id }}' + name: '{{ stage }}-{{ tenant_id }}' + description: '' + disabledFeatures: + - canvas + - maps + - ml + - visualize + - enterpriseSearch + - logs + - infrastructure + - apm + - uptime + - observabilityCases + - siem + - monitoring + - fleet + - stackAlerts + - actions + - osquery + - savedObjectsTagging + es_indexpattern_name: '{{ stage }}-{{ tenant_id }}-{{ cluster_name }}-*' + es_search_name: '{{ stage }}-{{ tenant_id }}-{{ cluster_name }}-{{ cluster_services_str }}' + es_dashboard_name: '{{ stage }}-{{ tenant_id }}-{{ cluster_name }}-{{ cluster_services_str }}' + + tasks: + - name: "Do some stuff in elastic with spaces ... " + include_role: + name: elastic + tasks_from: _configure_spaces.yml + apply: + tags: + - es-spaces + loop: "{{ elastic_spaces }}" + loop_control: + loop_var: elastic_space + tags: + - es-spaces + + - name: "Do some stuff in elastic with roles ... " + include_role: + name: elastic + tasks_from: _configure_roles.yml + apply: + tags: + - es-roles + loop: "{{ elastic_roles }}" + loop_control: + loop_var: elastic_role + tags: + - es-roles + + - name: "Do some stuff in elastic with users ... " + include_role: + name: elastic + tasks_from: _configure_users.yml + apply: + tags: + - es-users + loop: "{{ elastic_users }}" + loop_control: + loop_var: elastic_user + tags: + - es-users + + - name: "Do some stuff in elastic with spaces ... " + include_role: + name: elastic + tasks_from: _import_savedobjects.yml + apply: + tags: + - es-importobjects + vars: + es_space: *es_space_name + es_indexpattern_title: '{{ es_indexpattern_name }}' + es_indexpattern_uuid: '{{ es_indexpattern_name | to_uuid }}' + es_search_title: '{{ es_search_name }}' + es_search_uuid: '{{ es_search_name | to_uuid }}' + es_panel_uuid: "{{ 'panel_' + es_dashboard_name | to_uuid }}" + es_dashboard_title: '{{ es_dashboard_name }}' + es_dashboard_uuid: '{{ es_dashboard_name | to_uuid }}' + tags: + - es-importobjects diff --git a/roles/elastic/tasks/_configure_dashboards.yml b/roles/elastic/tasks/_configure_dashboards.yml new file mode 100644 index 0000000..db3543f --- /dev/null +++ b/roles/elastic/tasks/_configure_dashboards.yml @@ -0,0 +1,166 @@ +--- +- set_fact: + api_path: '/s/{{ es_space }}/api/saved_objects' + es_object_type: dashboard + dashboard_exists: False + elastic_dashboard_cleaned: {} + ref_obj_modified: {} + +- name: "Dashboards: Get all searches in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/_find?per_page=10000&type={{ es_object_type }}" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_dashboards + become: false + +- set_fact: + lookup_dashboard_object: '{{ all_dashboards.json | community.general.json_query(querystr1) | first | community.general.json_query(dashboard_query) }}' + vars: + querystr1: "[saved_objects[*]]" + dashboard_query: "[?attributes.title=='{{ elastic_dashboard.attributes.title }}']" + +- set_fact: + dashboard_exists: True + when: + - lookup_dashboard_object | length > 0 + +- set_fact: + elastic_dashboard_cleaned: "{{ elastic_dashboard_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_dashboard }}' + when: + - item.key not in ['elastic_state'] + + +### begin of block +- name: 'Dashboards: Lookup ID of search' + delegate_to: localhost + block: + - name: "Dashboards: Get all searches in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}/s/{{ es_space }}/api/saved_objects/_find?per_page=10000&type=search" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_searches + become: false + + - set_fact: + lookup_search_object: '{{ all_searches.json | community.general.json_query(querystr1) | first | community.general.json_query(search_query) }}' + vars: + querystr1: "[saved_objects[*]]" + search_query: "[?attributes.title=='{{ elastic_dashboard.references[0].search_refname }}']" + + - set_fact: + search_exists: True + when: + - lookup_search_object | length > 0 + + - debug: + msg: 'lookup_search_object{{ lookup_search_object }}' + + - set_fact: + panelindex_uuid: '{{ elastic_dashboard.references[0].search_refname | to_uuid }}' + + - name: "Doing evil string concatination with ansible in addition with variables" + delegate_to: localhost + set_fact: + panelsJSON: '{{ (''[{"version":"7.16.1","type":"search","gridData":{"x":0,"y":0,"w":48,"h":28,"i":"'' + ( panelindex_uuid | string ) + ''"},"panelIndex":"'' + ( panelindex_uuid | string ) + ''","embeddableConfig":{"enhancements":{}},"panelRefName":"panel_'' + ( panelindex_uuid | string ) + ''"}]'') | string }}' + + - set_fact: + ref_obj_modified: + attributes: + title: '{{ elastic_dashboard.attributes.title }}' + panelsJSON: '{{ panelsJSON | string }}' + references: + - + name: '{{ panelindex_uuid }}' + delegate_to: localhost + type: 'search' + id: '{{ lookup_search_object[0].id }}' + when: + - lookup_search_object | length > 0 + when: + - elastic_dashboard.elastic_state == 'present' +### end of block + +- name: "Dashboards: Kick out not needed keys in {{ es_object_type }}dict" + delegate_to: localhost + set_fact: + elastic_dashboard_cleaned: "{{ elastic_dashboard_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_dashboard }}' + when: + - item.key not in ['elastic_state','references'] + +- name: " Dashboards: Crafting new {{ es_object_type }} object to throw it against ES-API" + delegate_to: localhost + set_fact: + elastic_dashboard_cleaned: "{{ elastic_dashboard_cleaned | combine( ref_obj_modified ) }}" + +- debug: + msg: 'DEBUG elastic_dashboard_cleaned: {{ elastic_dashboard_cleaned }}' + + + + +- name: "Create {{ es_object_type }} <<{{ elastic_dashboard.attributes.title }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_dashboard_cleaned | to_json }}' + become: false + when: + - not dashboard_exists + - elastic_dashboard.elastic_state == 'present' + +- name: "Update {{ es_object_type }} <<{{ elastic_dashboard.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_dashboard_object[0]["id"] }}' + method: PUT + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_dashboard_cleaned | to_json }}' + become: false + when: + - dashboard_exists + - elastic_dashboard.elastic_state == 'present' + +- name: "DELETE {{ es_object_type }} <<{{ elastic_dashboard.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_dashboard_object[0]["id"] }}' + method: DELETE + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - dashboard_exists + - elastic_dashboard.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_configure_indexpattern.yml b/roles/elastic/tasks/_configure_indexpattern.yml new file mode 100644 index 0000000..cb12e06 --- /dev/null +++ b/roles/elastic/tasks/_configure_indexpattern.yml @@ -0,0 +1,90 @@ +--- +- set_fact: + api_path: '/s/{{ es_space }}/api/saved_objects' + es_object_type: 'index-pattern' + indexpattern_exists: False + elastic_indexpattern_cleaned: {} + +- name: "Get all indexpatterns in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/_find?per_page=10000&type=index-pattern" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_indexpatterns + become: false + +- set_fact: + lookup_indexpattern_object: '{{ all_indexpatterns.json | community.general.json_query(querystr1) | first | community.general.json_query(indexpattern_query) }}' + vars: + querystr1: "[saved_objects[*]]" + indexpattern_query: "[?attributes.title=='{{ elastic_indexpattern.attributes.title }}']" + +- set_fact: + indexpattern_exists: True + when: + - lookup_indexpattern_object | length > 0 + +- set_fact: + elastic_indexpattern_cleaned: "{{ elastic_indexpattern_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_indexpattern }}' + when: + - item.key not in ['elastic_state'] + +- name: "Create {{ es_object_type }} <<{{ elastic_indexpattern.attributes.title }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_indexpattern_cleaned | to_json }}' + become: false + when: + - not indexpattern_exists + - elastic_indexpattern.elastic_state == 'present' + +- name: "Update {{ es_object_type }} <<{{ elastic_indexpattern.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_indexpattern_object[0]["id"] }}' + method: PUT + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_indexpattern_cleaned | to_json }}' + become: false + when: + - indexpattern_exists + - elastic_indexpattern.elastic_state == 'present' + +- name: "DELETE {{ es_object_type }} <<{{ elastic_indexpattern.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_indexpattern_object[0]["id"] }}' + method: DELETE + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - indexpattern_exists + - elastic_indexpattern.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_configure_roles.yml b/roles/elastic/tasks/_configure_roles.yml new file mode 100644 index 0000000..4d7b513 --- /dev/null +++ b/roles/elastic/tasks/_configure_roles.yml @@ -0,0 +1,88 @@ +--- +- set_fact: + api_path: '/api/security/role' + role_exists: False + elastic_role_cleaned: {} + +- name: "Get all roles in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_roles + become: false + +- set_fact: + lookup_role_object: '{{ all_roles.json | community.general.json_query(roles_query) }}' + vars: + roles_query: "[?name=='{{ elastic_role.name }}']" + +- set_fact: + role_exists: True + when: + - lookup_role_object | length > 0 + +- set_fact: + elastic_role_cleaned: "{{ elastic_role_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_role }}' + when: + - item.key not in ['elastic_state','name'] + +- name: "Create role <<{{ elastic_role.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_role.name }}" + method: PUT + status_code: [204] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_role_cleaned | to_json }}' + become: false + when: + - not role_exists + - elastic_role.elastic_state == 'present' + +- name: "Update role <<{{ elastic_role.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_role.name }}" + method: PUT + status_code: [204] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_role_cleaned | to_json }}' + become: false + when: + - role_exists + - elastic_role.elastic_state == 'present' + +- name: "DELETE role <<{{ elastic_role.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_role.name }}" + method: DELETE + status_code: [204] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - role_exists + - elastic_role.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_configure_searches.yml b/roles/elastic/tasks/_configure_searches.yml new file mode 100644 index 0000000..696e610 --- /dev/null +++ b/roles/elastic/tasks/_configure_searches.yml @@ -0,0 +1,152 @@ +--- +- set_fact: + api_path: '/s/{{ es_space }}/api/saved_objects' + es_object_type: search + search_exists: False + elastic_search_cleaned: {} + ref_obj_modified: {} + +- name: "Get all searches in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/_find?per_page=10000&type=search" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_searches + become: false + +- set_fact: + lookup_search_object: '{{ all_searches.json | community.general.json_query(querystr1) | first | community.general.json_query(search_query) }}' + vars: + querystr1: "[saved_objects[*]]" + search_query: "[?attributes.title=='{{ elastic_search.attributes.title }}']" + +- set_fact: + search_exists: True + when: + - lookup_search_object | length > 0 + +- set_fact: + elastic_search_cleaned: "{{ elastic_search_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_search }}' + when: + - item.key not in ['elastic_state'] + +### begin of block +- name: 'Lookup ID of indexpattern' + delegate_to: localhost + block: + - name: "Get all indexpatterns in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}/s/{{ es_space }}/api/saved_objects/_find?per_page=10000&type=index-pattern" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_indexpatterns + become: false + + - set_fact: + lookup_indexpattern_object: '{{ all_indexpatterns.json | community.general.json_query(querystr1) | first | community.general.json_query(indexpattern_query) }}' + vars: + querystr1: "[saved_objects[*]]" + indexpattern_query: "[?attributes.title=='{{ elastic_search.references[0].ref_name }}']" + + - set_fact: + indexpattern_exists: True + when: + - lookup_indexpattern_object | length > 0 + + - debug: + msg: 'lookup_indexpattern_object:{{ lookup_indexpattern_object }}' + + - set_fact: + ref_obj_modified: + references: + - + name: '{{ elastic_search.references[0].name }}' + delegate_to: localhost + type: 'index-pattern' + id: '{{ lookup_indexpattern_object[0].id }}' + when: + - lookup_indexpattern_object | length > 0 + when: + - elastic_search.elastic_state == 'present' +### end of block + +- name: "Kick out not needed keys in search dict" + delegate_to: localhost + set_fact: + elastic_search_cleaned: "{{ elastic_search_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_search }}' + when: + - item.key not in ['elastic_state','references'] + +- name: "Crafting new search object to throw it against ES-API" + delegate_to: localhost + set_fact: + elastic_search_cleaned: "{{ elastic_search_cleaned | combine( ref_obj_modified ) }}" + +- debug: + msg: 'DEBUG elastic_search_cleaned: {{ elastic_search_cleaned }}' + + +- name: "Create {{ es_object_type }} <<{{ elastic_search.attributes.title }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_search_cleaned | to_json }}' + become: false + when: + - not search_exists + - elastic_search.elastic_state == 'present' + +- name: "Update {{ es_object_type }} <<{{ elastic_search.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_search_object[0]["id"] }}' + method: PUT + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_search_cleaned | to_json }}' + become: false + when: + - search_exists + - elastic_search.elastic_state == 'present' + +- name: "DELETE {{ es_object_type }} <<{{ elastic_search.attributes.title }}>>" + delegate_to: localhost + uri: + url: 'https://{{ api_endpoint }}{{ api_path }}/{{ es_object_type }}/{{ lookup_search_object[0]["id"] }}' + method: DELETE + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - search_exists + - elastic_search.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_configure_spaces.yml b/roles/elastic/tasks/_configure_spaces.yml new file mode 100644 index 0000000..0458d2d --- /dev/null +++ b/roles/elastic/tasks/_configure_spaces.yml @@ -0,0 +1,91 @@ +--- +- set_fact: + api_path: '/api/spaces/space' + space_exists: False + elastic_space_cleaned: {} + +- name: "Get all spaces in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_spaces + become: false + +- set_fact: + lookup_space_object: '{{ all_spaces.json | community.general.json_query(spaces_query) }}' + vars: + spaces_query: "[?name=='{{ elastic_space.name }}']" + +- set_fact: + space_exists: True + when: + - lookup_space_object | length > 0 + +- set_fact: + elastic_space_cleaned: "{{ elastic_space_cleaned | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_space }}' + when: + - item.key not in ['elastic_state'] + +- debug: + msg: '{{ lookup_space_object | to_json }}' + +- name: "Create space <<{{ elastic_space.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_space_cleaned | to_json }}' + become: false + when: + - not space_exists + - elastic_space.elastic_state == 'present' + +- name: "Update space <<{{ elastic_space.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_space.name }}" + method: PUT + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_space_cleaned | to_json }}' + become: false + when: + - space_exists + - elastic_space.elastic_state == 'present' + +- name: "DELETE space <<{{ elastic_space.name }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_space.name }}" + method: DELETE + status_code: [204] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - space_exists + - elastic_space.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_configure_users.yml b/roles/elastic/tasks/_configure_users.yml new file mode 100644 index 0000000..bd55e91 --- /dev/null +++ b/roles/elastic/tasks/_configure_users.yml @@ -0,0 +1,96 @@ +--- +- set_fact: + api_path: '/internal/security/users' + user_exists: False + elastic_user_cleaned__create: {} + elastic_user_cleaned__update: {} + +- name: "Get all users in elasticsearch" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}" + method: GET + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + register: all_users + become: false + +- set_fact: + lookup_user_object: '{{ all_users.json | community.general.json_query(users_query) }}' + vars: + users_query: "[?username=='{{ elastic_user.username }}']" + +- set_fact: + user_exists: True + when: + - lookup_user_object | length > 0 + +- set_fact: + elastic_user_cleaned__create: "{{ elastic_user_cleaned__create | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_user }}' + when: + - item.key not in ['elastic_state'] + +# make sure to not override userdefined password with initial password +- set_fact: + elastic_user_cleaned__update: "{{ elastic_user_cleaned__update | combine({item.key: item.value}) }}" + with_dict: '{{ elastic_user_cleaned__create }}' + when: + - item.key not in ['password'] + +- name: "Create user <<{{ elastic_user.username }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_user.username }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_user_cleaned__create | to_json }}' + become: false + when: + - not user_exists + - elastic_user.elastic_state == 'present' + +- name: "Update user <<{{ elastic_user.username }}>>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_user.username }}" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + body_format: json + body: '{{ elastic_user_cleaned__update | to_json }}' + become: false + when: + - user_exists + - elastic_user.elastic_state == 'present' + +- name: "DELETE user << elastic_user.username >>" + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/{{ elastic_user.username }}" + method: DELETE + status_code: [204] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + Content-Type: application/json + kbn-xsrf: true + become: false + when: + - user_exists + - elastic_user.elastic_state == 'absent' diff --git a/roles/elastic/tasks/_import_savedobjects.yml b/roles/elastic/tasks/_import_savedobjects.yml new file mode 100644 index 0000000..e33e79c --- /dev/null +++ b/roles/elastic/tasks/_import_savedobjects.yml @@ -0,0 +1,66 @@ +--- +- set_fact: + api_path: '/s/{{ es_space }}/api/saved_objects' + +- name: "Import smardigo default dashboard and its related objects (index-pattern,search)" + delegate_to: localhost + set_fact: + es_import_objects: "{{ lookup('template','smardigo_default_objects.json.j2') }}" + when: + - elastic_state == 'present' + +- name: "Print objects to local file" + delegate_to: localhost + copy: + dest: '/tmp/es_objects_ready_to_import__objects.ndjson' + content: '{{ es_import_objects }}' + when: + - elastic_state == 'present' + +- name: "Import elastic objects ..." + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}{{ api_path }}/_import?overwrite=true" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + kbn-xsrf: true + body_format: form-multipart + body: + file: + filename: '/tmp/es_objects_ready_to_import__objects.ndjson' + mime_type: 'application/octet-stream' + become: false + when: + - elastic_state == 'present' + +- name: "Remove temporarily created file" + delegate_to: localhost + file: + state: absent + path: '/tmp/es_objects_ready_to_import__objects.ndjson' + when: + - elastic_state == 'present' + + +- name: "Set default indexpattern ..." + delegate_to: localhost + uri: + url: "https://{{ api_endpoint }}/s/dev-fgrz/api/kibana/settings" + method: POST + status_code: [200] + user: "{{ elastic_admin_username_vault }}" + password: "{{ elastic_admin_password_vault }}" + force_basic_auth: yes + headers: + kbn-xsrf: true + body_format: json + body: + changes: + defaultIndex: '{{ es_indexpattern_uuid }}' + become: false + when: + - elastic_state == 'present' diff --git a/roles/elastic/templates/smardigo_default_objects.json.j2 b/roles/elastic/templates/smardigo_default_objects.json.j2 new file mode 100644 index 0000000..131fb8f --- /dev/null +++ b/roles/elastic/templates/smardigo_default_objects.json.j2 @@ -0,0 +1,3 @@ +{"attributes":{"fieldAttrs":"{}","fields":"[]","runtimeFieldMap":"{}","timeFieldName":"@timestamp","title":"{{ es_indexpattern_title }}","typeMeta":"{}"},"coreMigrationVersion":"7.16.1","id":"{{ es_indexpattern_uuid }}","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"type":"index-pattern"} +{"attributes":{"columns":[],"description":"","grid":{},"hideChart":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["@timestamp","desc"]],"title":"{{ es_search_title }}"},"coreMigrationVersion":"7.16.1","id":"{{ es_search_uuid }}","migrationVersion":{"search":"7.9.3"},"references":[{"id":"{{ es_indexpattern_uuid }}","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"search"} +{"attributes":{"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"version\":\"7.16.1\",\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":0,\"w\":50,\"h\":50,\"i\":\"{{ es_panel_uuid }}\"},\"panelIndex\":\"{{ es_panel_uuid }}\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_{{ es_panel_uuid }}\"}]","timeRestore":false,"title":"{{ es_dashboard_title }}","version":1},"coreMigrationVersion":"7.16.1","id":"{{ es_dashboard_uuid }}","migrationVersion":{"dashboard":"7.16.0"},"references":[{"id":"{{ es_search_uuid }}","name":"{{ es_panel_uuid }}:panel_{{ es_panel_uuid }}","type":"search"}],"type":"dashboard"}