Merge branch 'genesis' into 'development'

feat: new role

See merge request nofusscomputing/projects/ansible/nfc_glpi!1
This commit is contained in:
2023-11-06 08:21:17 +00:00
38 changed files with 1593 additions and 0 deletions

7
.cz.yaml Normal file
View File

@ -0,0 +1,7 @@
commitizen:
bump_message: "build(version): bump version $current_version \u2192 $new_version"
changelog_incremental: false
name: cz_conventional_commits
tag_format: $major.$minor.$patch$prerelease
update_changelog_on_bump: true
version: 0.0.1

15
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,15 @@
---
include:
- project: nofusscomputing/projects/gitlab-ci
ref: development
file:
- .gitlab-ci_common.yaml
- template/automagic.gitlab-ci.yaml
variables:
MY_PROJECT_ID: "48046854"
GIT_SYNC_URL: "https://$GITHUB_USERNAME_ROBOT:$GITHUB_TOKEN_ROBOT@github.com/NoFussComputing/ansible_role_glpi.git"
PAGES_ENVIRONMENT_PATH: projects/nfc_glpi/

8
.gitmodules vendored Normal file
View File

@ -0,0 +1,8 @@
[submodule "gitlab-ci"]
path = gitlab-ci
url = https://gitlab.com/nofusscomputing/projects/gitlab-ci.git
branch = development
[submodule "website-template"]
path = website-template
url = https://gitlab.com/nofusscomputing/infrastructure/website-template.git
branch = development

8
.nfc_automation.yaml Normal file
View File

@ -0,0 +1,8 @@
---
role_git_conf:
gitlab:
submodule_branch: "development"
default_branch: development
mr_labels: ~"type::automation" ~"impact::0" ~"priority::0"
auto_merge: true

51
defaults/main.yaml Normal file
View File

@ -0,0 +1,51 @@
---
# directory_logrotate_config: /etc/logrotate.d
install_glpi: 'true'
glpi_installed: false
# logfile_max_size: 10 # must be specified in MB (Mega Bytes)
# logrotate_configuration: [] # Global var for usage within group/host vars
docker_image_name_glpi: nofusscomputing/docker-glpi
docker_image_tag_glpi: dev
docker_container_name_glpi: glpi
# mysql_unix_socket: /run/mysql/mariadb/mysqld.sock
mysql_unix_socket: /var/lib/docker/volumes/socket-mysql/_data/mysqld.sock
mysql_login_user: root
mysql_login_password: admin
glpi_plugins:
- filename: fail2ban_1.0.2-1.upstream1_all.deb
#url: https://github.com/pluginsGLPI/formcreator/releases/download/2.13.6/glpi-formcreator-2.13.6.tar.bz2
url: https://api.github.com/repos/pluginsGLPI/formcreator/releases/tags/2.13.6
- filename: glpi-glpiinventory-1.2.3.tar.bz2
#url: https://github.com/glpi-project/glpi-inventory-plugin/releases/download/1.2.3/glpi-glpiinventory-1.2.3.tar.bz2
url: https://api.github.com/repos/glpi-project/glpi-inventory-plugin/releases/tags/1.2.3
# - filename: glpi-actualtime-2.1.0.tar.tgz
# url: https://api.github.com/repos/ticgal/actualtime/releases/tags/2.1.0/glpi-actualtime-2.1.0.tar.tgz
mysql_database_glpi: glpi
# Configuration Example
# logrotate:
# - name: docker # Mandatory, used as logrotate difinition file name
# path: '/var/lib/docker/containers/*/*.log' # Mandatory, path to the logfile
# su: ingress ingress # Optional,, string user and group for log permissions
# interval: daily # optional example daily, weekly, monthly default=daily
# keep: # Optional, int, How many logs to keep default=7
# size: 10 # Optional, max size of logfile in Mega Bytes. DONT specify unit. default={{}}
# compress: # Optional, bool default=true
# delaycompress: # Optional, bool, delay logfile compression default=true
# postrotate: 'docker restart $(docker ps -q)' # Optional, Command to run post rotate default=not defined
# create: # Optional, default=not defined
# mode: 640 # Mandatory, filemode
# owner: root # Mandatory, logfile owner
# group: adm # Mandatory, logfile group owner
glpi_config_as_code_json: []
search_items: []

0
docs/articles/index.md Normal file
View File

0
docs/contact.md Normal file
View File

0
docs/index.md Normal file
View File

0
docs/operations/index.md Normal file
View File

0
docs/projects/index.md Normal file
View File

View File

@ -0,0 +1,170 @@
---
title: GLPI Config as code
description: How to use No Fuss Computings Ansible role to configure GLPI from configuration as code.
date: 2023-07-28
template: project.html
about: https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi
---
To configure GLPI using this role, the config/settings are saved as json files. The workflow to create the json files, is to (preferably within a test environment) make the required setting changes in GLPI using the GUI. once the changes have been made, conduct an API `GET` query to GLPI for the item you changed. The JSON body that is returned, is placed within the Config file (detailed below) under path `.body`. You must however, remove any fields that are not required, for example dynamic fields (fields that update themselves or are auto created) for example date fields. it's also recommended to remove the items `id` entry from the body, this option enables the updating and creation of the item by the name field.
As the export process is manual to create the json files, it is possible that errors could occur. another side effect is that when updating an item, when checking it into an SCM (git for example), that the commit may contain more than the item that changed. This is planned to be rectified in [gitlab-#1](https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi/-/issues/1).
!!! warning
To utilise the config from json files, it's important that any configurable item from json files, that it's name is unique for that item type within GLPI. This is due to the feature "find item_id by name" i.e. you can't have two items named `my name` and `my name` of type `x`, even if in different entities.
## Config files Example
Each tab provides an example of the JSON file layout, including any additional variables. Variable listed at path `.`, with the exception of `api_path`, `body` or have asuffix of `_` are provided to use that items name. These fields are used to fetch the item_id of the field by name. available fields are:
- entities_id
- groups_id
- item_id
- itilcategories_id
- tickettemplates_id_demand
- tickettemplates_id
- users_id
For example, to have an item placed in the entity `NoFussComputing` provided value would be `"entities_id": "NoFussComputing"`. As part of the item workflow a search for an entity named `NoFussComputing` is conducted, and if found, adds it's `entity_id` to path `.body.entities_id: {entity_id}`.
!!! tip
If you leave the item ID in the config file under path `.body.id`, the item that matches this ID, will be updated, not the item matching the name.
=== "AuthLDAP"
``` json title="example.json" linenums="1"
{
"api_path": "AuthLDAP",
"body": {
// JSON from API Query
}
}
```
=== "Config (GLPI General Settings)"
This config file contains the settings found in `General Settings`. The full API JSON response is added to the body. When this file is processed, each individual item is added via the API without using the `id` as found in the JSON file. This is done as the ID's can change, so the workflow includes a step that matches the config item name to the found id in the database, then patches with the values from the JSON file.
``` json title="example.json" linenums="1"
{
"api_path": "Config",
"body": [
// JSON from API Query the full response (see note below)
]
}
```
!!! info
The structure of this JSON file is different than the rest. The body is a **list** of **ALL** of the Config options. This includes the item ID. it was done this way, so that only one API query is required to export all config options.
!!! info "Further Information"
Any config item that does not contain a value field, is excluded from being added or updated. In addition, the following items by name, are excluded
``` yaml title="tasks/api/excluded_config.yaml" linenums="1"
--8<-- "tasks/api/excluded_config.yaml"
```
=== "Entity"
``` json title="example.json" linenums="1"
{
"api_path": "Entity",
"entities_id": "",
"body": {
// JSON from API Query
}
}
```
=== "ITILCategory"
``` json title="example.json" linenums="1"
{
"api_path": "ITILCategory",
"users_id": "",
"itilcategories_id": "",
"entities_id": "",
"tickettemplates_id_demand": "",
"body": {
// JSON from API Query
}
}
```
=== "Profile"
``` json title="example.json" linenums="1"
{
"api_path": "Profile",
"body": {
// JSON from API Query
}
}
```
=== "TicketTemplate"
This config file covers all parts of a ticket template. with this template you can create and update an existing ticket template.
!!! warning
Assosiated Items do not get updated or removed. They will be added if they don't exist. No checks are done for more assosiated items then are specified in the template.
``` json title="example.json" linenums="1"
{
"api_path": "TicketTemplate",
"entities_id": "",
"body": {
// JSON from API Query
},
"_TicketTemplateMandatoryField": [
// JSON from API Query path TicketTemplate/{ticket_template_id}/TicketTemplateMandatoryField
],
"_TicketTemplatePredefinedField": [
// JSON from API Query path TicketTemplate/{ticket_template_id}/TicketTemplatePredefinedField
],
"_TicketTemplateHiddenField": [
// JSON from API Query path TicketTemplate/{ticket_template_id}/TicketTemplateHiddenField
]
}
```
Sub-items to the ticket are `TicketTemplateMandatoryField`, `TicketTemplatePredefinedField` and `TicketTemplateHiddenField` and are at json path `._{sub-item}`. These items are a **list of dicts** and can contain the `id` of the item, however it is ignored as the update is base off of `tickettemplates_id`, `num` for all and `tickettemplates_id`, `num` and `value` for `num=13` which is an assosiated item. With the exception of the latter, all of these values will be updated if they exist.
With the config files structured as per the examples, within your playbook(s), load the json files in a list of json objects using variable `glpi_config_as_code_json`
example
``` yaml title="my_vars.yaml" linenums="1"
glpi_config_as_code_json:
- "{{ lookup('file', '/workdir/files/glpi/ITILCategory-new-ldap-user.json') | from_json }}"
- "{{ lookup('file', '/workdir/files/glpi/LDAPAuth-local_ldap.json') | from_json }}"
```

View File

@ -0,0 +1,25 @@
---
title: nfc_glpi - Ansible Role
description: How to use No Fuss Computings Ansible role to manage GLPI from configuration as code.
date: 2023-07-28
template: project.html
about: https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi
---
This Ansible role is designed to manage GLPI specifically installation and configuration.
## Features
- install GLPI using our [docker container](https://gitlab.com/nofusscomputing/projects/docker-glpi)
- [Configure using Config from Code](config_from_code.md)
## Default Variables
``` yaml title="defaults/main.yaml" linenums="1"
--8<-- "defaults/main.yaml"
```

1
gitlab-ci Submodule

Submodule gitlab-ci added at 975de7aca2

29
handlers/main.yaml Normal file
View File

@ -0,0 +1,29 @@
---
# - name: Log Rotate Configuration files
# ansible.builtin.template:
# src: definition.j2
# dest: "{{ directory_logrotate_config }}/{{ item.name }}"
# owner: root
# mode: '0644'
# force: true
# loop: "{{ logrotate_configuration }}"
# listen: 'configure_logrotate'
# notify: restart_logrotate
# when: logrotate_installed | bool
# - name: Restart logrotate
# ansible.builtin.service:
# name: logrotate
# state: restarted
# listen: restart_logrotate
# when: logrotate_installed | bool
- name: Restart GLPI
community.docker.docker_container:
name: "{{ docker_container_name_glpi }}"
state: started
restart: true
listen: restart_glpi
when: >
glpi_installed | bool

13
meta/main.yaml Normal file
View File

@ -0,0 +1,13 @@
galaxy_info:
role_name: nfc_glpi
author: No Fuss Computing
description: |-
A universal role to install and configure a glpi.
This role is also desiged so that it can be included within other roles.
issue_tracker_url: https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi
license: https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi/-/blob/master/LICENSE
min_ansible_version: '1.2'

33
mkdocs.yml Normal file
View File

@ -0,0 +1,33 @@
INHERIT: website-template/mkdocs.yml
docs_dir: 'docs'
repo_name: nfc_glpi
repo_url: https://gitlab.com/nofusscomputing/projects/ansible/nfc_glpi
edit_uri: '/-/ide/project/nofusscomputing/projects/ansible/nfc_glpi/edit/development/-/docs/'
nav:
- Home: index.md
- Articles:
- articles/index.md
- Projects:
- projects/index.md
- Ansible Roles:
- GLPI:
- projects/nfc_glpi/index.md
- projects/nfc_glpi/config_from_code.md
- Operations:
- operations/index.md
- Contact Us: contact.md

24
tasks/api/api.yaml Normal file
View File

@ -0,0 +1,24 @@
---
- name: Always End Session
block:
- name: Start Session
ansible.builtin.include_tasks:
file: api/session-create.yaml
when: glpi.api.session | default('') == ''
- name: Append/Create Item
ansible.builtin.include_tasks:
file: search-add.yaml
loop: "{{ glpi_config_as_code_json }}"
always:
- name: End Session
ansible.builtin.include_tasks:
file: api/session-end.yaml
when: glpi_session_get.status | default(0) | int == 200

View File

@ -0,0 +1,73 @@
---
- name: Show item
ansible.builtin.debug:
msg: "{{ item }}"
- name: Create item_body
ansible.builtin.set_fact:
item_body: "{{ item.body }}"
no_log: true
- name: Search for item IDs
ansible.builtin.include_tasks:
file: api/search.yaml
- name: Show Body
ansible.builtin.debug:
msg: "{{ item_body }}"
when: item_body is defined
- name: Create Item
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/{{ item.api_path }}{% if item.sub_path is defined %}/{{ item.sub_path }}{% endif %}"
method: "POST"
return_content: true
body: "{\"input\": {{ item_body | from_yaml | to_json }} }"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
no_log: true
register: create_item
when: >
not item_body.id is defined
- name: Update Item
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/{{ item.api_path }}{% if item.sub_path is defined %}/{{ item.sub_path }}{% else %}{% if item.api_path != 'Config' %}/{{ item_body.id | default('') }}{% endif %}{% endif %}"
method: "PATCH"
return_content: true
body: "{\"input\": {{ item_body | from_yaml | to_json }} }"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
no_log: true
register: update_item
when: item_body.id is defined
- name: Fail on no action
ansible.builtin.fail:
msg: No action carried out, stop.
when: >
update_item.skipped | default(false) | bool
and
create_item.skipped | default(false) | bool
- name: Clear temp vars
ansible.builtin.set_fact:
item_body: {}
create_item: {}
update_item: {}
no_log: true

100
tasks/api/config.yaml Normal file
View File

@ -0,0 +1,100 @@
---
- name: Fetch ID for {{ list_item.body.name }}
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/{{ item.api_path }}?searchText[name]={{ list_item.body.name | urlencode }}"
method: "GET"
return_content: true
body: ''
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
no_log: true
register: glpi_search
- name: Build API Body for {{ list_item.body.name }}
ansible.builtin.set_fact:
config_item:
api_path: "{{ list_item.api_path }}"
body:
context: "{{ list_item.body.context }}"
name: "{{ list_item.body.name }}"
value: "{{ list_item.body.value }}"
when: glpi_search.json | length | int == 0
no_log: true
- name: Build API Body (with ID) for {{ list_item.body.name }}
ansible.builtin.set_fact:
config_item:
api_path: "{{ list_item.api_path }}"
body:
id: "{% for found_id in glpi_search.json %}{%if list_item.body.name == found_id.name %}{{ found_id.id }}{% endif %}{% endfor %}"
context: "{{ list_item.body.context }}"
name: "{{ list_item.body.name }}"
value: "{{ list_item.body.value }}"
when: glpi_search.json | length | int > 0
no_log: true
- name: Remove Empty ID from body
ansible.builtin.set_fact:
config_item:
api_path: "{{ list_item.api_path }}"
body:
context: "{{ list_item.body.context }}"
name: "{{ list_item.body.name }}"
value: "{{ list_item.body.value }}"
when: config_item.body.id | default('0') == ''
no_log: true
- name: "Item [{{ config_item.body.id + ']: ' + config_item.body.name }}"
ansible.builtin.debug:
msg: "{{ config_item }}"
when: config_item is defined
# ToDo: figure out why cant create item?????
# - name: Create Config Item
# ansible.builtin.uri:
# url: "http://{{ glpi.host }}/apirest.php/{{ config_item.api_path }}"
# method: "POST"
# return_content: true
# body: "{\"input\": {{ config_item.body | from_yaml | to_json }} }"
# status_code: [200, 201]
# headers:
# App-Token: "{{ glpi.app_token }}"
# Session-Token: "{{ glpi.session.valid_id }}"
# body_format: json
# # no_log: true
# when: not config_item.body.id is defined
- name: Update Config Item
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/{{ item.api_path }}/{{ config_item.body.id }}"
method: "PATCH"
return_content: true
body: "{\"input\": {{ config_item.body | from_yaml | to_json }} }"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
no_log: true
when: config_item.body.id is defined
- name: Clear temp vars
ansible.builtin.set_fact:
config_item: {}
glpi_search: {}
list_item: {}
no_log: true

View File

@ -0,0 +1,21 @@
---
#
# Config items excluded from Adding/Updating.
#
# Each list item is the name of the config item.
#
- name: Config Items to skip
ansible.builtin.set_fact:
skip_config:
- cas_version
- dbversion
- _dbslave_status
- init_all
- instance_uuid
- notification_uuid
- proxy_passwd
- registration_uuid
- schema_version
- smtp_passwd
- version
when: item.api_path == 'Config'

45
tasks/api/search-add.yaml Normal file
View File

@ -0,0 +1,45 @@
---
- name: Ticket Template
ansible.builtin.include_tasks:
file: api/ticket_template.yaml
when: item.api_path == 'TicketTemplate'
- name: Append/Create Item
ansible.builtin.include_tasks:
file: api/append-create-item.yaml
when: >
not item.api_path == 'Config'
and
not item.api_path == 'TicketTemplate'
- name: Config Items to skip
ansible.builtin.import_tasks:
file: api/excluded_config.yaml
- name: Add/Update GLPI Config
ansible.builtin.include_tasks:
file: api/config.yaml
when: >
not sub_item.name in skip_config
and
sub_item.value is defined
loop: "{% if item.api_path == 'Config' %}{{ item.body }}{% else %}{{ [] }}{% endif %}"
loop_control:
loop_var: sub_item
vars:
list_item:
api_path: "{{ item.api_path }}"
body:
context: "{{ sub_item.context }}"
name: "{{ sub_item.name }}"
value: "{{ sub_item.value }}"
- name: Clear temp vars
ansible.builtin.set_fact:
item_body: {}
no_log: true

42
tasks/api/search.yaml Normal file
View File

@ -0,0 +1,42 @@
---
- name: Search item_id
ansible.builtin.include_tasks:
file: api/search/item_id.yaml
when: not item.body.id is defined
- name: Search entities_id
ansible.builtin.include_tasks:
file: api/search/entities_id.yaml
when: item.entities_id is defined
- name: Search groups_id
ansible.builtin.include_tasks:
file: api/search/groups_id.yaml
when: item.groups_id is defined
- name: Search itilcategories_id
ansible.builtin.include_tasks:
file: api/search/itilcategories_id.yaml
when: item.itilcategories_id is defined
- name: Search tickettemplates_id_demand
ansible.builtin.include_tasks:
file: api/search/tickettemplates_id_demand.yaml
when: item.tickettemplates_id_demand is defined
- name: Search tickettemplates_id
ansible.builtin.include_tasks:
file: api/search/tickettemplates_id.yaml
when: item.tickettemplates_id is defined
- name: Search users_id
ansible.builtin.include_tasks:
file: api/search/users_id.yaml
when: item.users_id is defined

View File

@ -0,0 +1,54 @@
---
- name: Fetch entities_id
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/Entity?searchText[name]={{ item.entities_id | urlencode }}"
method: "GET"
return_content: true
body: ''
# status_code: "{{ item.status_code | from_yaml | list }}"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.entities_id | string -%}
"entities_id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set entities_id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append entities_id
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,54 @@
---
- name: Fetch users_id
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/Group?searchText[name]={{ item.groups_id | urlencode }}"
method: "GET"
return_content: true
body: ''
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.groups_id | string -%}
"groups_id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set groups_id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append groups (have item_body)
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,122 @@
---
- name: Find Item ID
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/{{ item.api_path }}{% if item.sub_path is defined %}/{{ item.sub_path }}{% endif%}{% if search_items | length | int > 0%}?{% for search in search_items %}searchText[{{ search.name }}]={{ search.value | urlencode }}&{% endfor %}{% else%}?searchText[name]={{ item.body.name | urlencode }}{% endif %}"
method: "GET"
return_content: true
body: ''
# status_code: "{{ item.status_code | from_yaml | list }}"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Set id
ansible.builtin.set_fact:
new_data:
id: "{{ glpi_search.json[0].id | int }}"
when: glpi_search.json | length | int == 1
- name: Iterate over results for TicketTemplateMandatoryField id/TicketTemplateHiddenField
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if
json.tickettemplates_id | int == item_body.tickettemplates_id | int
and
json.num | int == item_body.num | int
-%}
"id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 1
and
(
'TicketTemplateMandatoryField' in item.sub_path | default('')
or
'TicketTemplateHiddenField' in item.sub_path | default('')
)
- name: Set iterate id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout is defined
- name: Iterate over results for TicketTemplatePredefinedField id
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if
json.num | int == 13
and
json.tickettemplates_id | int == item_body.tickettemplates_id | int
and
json.num | int == item_body.num | int
and
json.value == item_body.value
-%}
"id": {{ json.id | int }}
{%- elif
json.num | int != 13
and
json.tickettemplates_id | int == item_body.tickettemplates_id | int
and
json.num | int == item_body.num | int
-%}
"id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 1
and
'TicketTemplatePredefinedField' in item.sub_path | default('')
- name: Set iterate id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout is defined
- name: Item_id fact Cleanup
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,54 @@
---
- name: Fetch itilcategories_id
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/ITILCategory?searchText[name]={{ item.itilcategories_id | urlencode }}"
method: "GET"
return_content: true
body: ''
# status_code: "{{ item.status_code | from_yaml | list }}"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.itilcategories_id | string -%}
"itilcategories_id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set itilcategories_id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append itilcategories_id
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,54 @@
---
- name: Fetch tickettemplates_id
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/TicketTemplate?searchText[name]={{ item.tickettemplates_id | urlencode }}"
method: "GET"
return_content: true
body: ''
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.tickettemplates_id | string -%}
"tickettemplates_id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set tickettemplates_id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append tickettemplates_id
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
tickettemplates_id: "{{ new_data.tickettemplates_id | default(omit) | int }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,54 @@
---
- name: Fetch tickettemplates_id_demand
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/TicketTemplate?searchText[name]={{ item.tickettemplates_id_demand | urlencode }}"
method: "GET"
return_content: true
body: ''
# status_code: "{{ item.status_code | from_yaml | list }}"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.tickettemplates_id_demand | string -%}
"tickettemplates_id_demand": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set tickettemplates_id_demand
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append tickettemplates_id_demand
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,55 @@
---
- name: Fetch users_id
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/User?searchText[name]={{ item.users_id | urlencode }}"
method: "GET"
return_content: true
body: ''
# status_code: "{{ item.status_code | from_yaml | list }}"
status_code: [200, 201]
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_search
no_log: true
- name: Iterate over results for ID search
ansible.builtin.shell:
cmd: |
cat <<EOF
{
{%- for json in glpi_search.json -%}
{%- if json.name | string == item.users_id | string -%}
"users_id": {{ json.id | int }}
{%- endif -%}
{%- endfor -%}
}
EOF
args:
executable: bash
register: itarate_id
changed_when: false
no_log: true
when: >
glpi_search.json | length | int > 0
- name: Set users_id
ansible.builtin.set_fact:
new_data: "{{ itarate_id.stdout | from_yaml }}"
when: itarate_id.stdout | default({}) | length | int > 0
- name: Append users_id (have item_body)
ansible.builtin.set_fact:
item_body: "{{ item_body | ansible.builtin.combine(new_data | default({})) }}"
glpi_search: {}
itarate_id: {}
new_data: {}
no_log: true

View File

@ -0,0 +1,30 @@
---
- name: Fetch API Session Token
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/initSession?get_full_session=true"
method: GET
return_content: true
body: ""
status_code: 200
headers:
App-Token: "{{ glpi.app_token }}"
Authorization: "user_token {{ glpi.user_token }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
register: glpi_session_get
no_log: true
tags:
- always
- name: Session Data
ansible.builtin.set_fact:
glpi_session:
session: "{{ glpi_session_get.json.session }}"
no_log: true
- name: Session fact
ansible.builtin.set_fact:
glpi: "{{ glpi | ansible.builtin.combine(glpi_session) }}"
no_log: true

View File

@ -0,0 +1,14 @@
---
- name: End API Session
ansible.builtin.uri:
url: "{{ http_protocol | default('https') | string }}://{{ glpi.host }}/apirest.php/killSession"
method: GET
return_content: true
body: ''
status_code: 200
headers:
App-Token: "{{ glpi.app_token }}"
Session-Token: "{{ glpi_session_get.json.session.valid_id }}"
body_format: json
validate_certs: "{{ glpi.validate_certs | default(true) | bool }}"
no_log: true

View File

@ -0,0 +1,103 @@
---
- name: Template item
ansible.builtin.set_fact:
template_item: "{{ item }}"
- name: Append/Create TicketTemplate
ansible.builtin.include_tasks:
file: api/append-create-item.yaml
when: item.api_path == 'TicketTemplate'
vars:
item:
api_path: TicketTemplate
entities_id: "{{ template_item.entities_id }}"
body: "{{ template_item.body }}"
- name: Search tickettemplates_id
ansible.builtin.include_tasks:
file: api/search/tickettemplates_id.yaml
vars:
item:
tickettemplates_id: "{{ template_item.body.name }}"
- name: Append/Create Ticket Template Mandatory Fields
ansible.builtin.include_tasks:
file: api/append-create-item.yaml
when: >
template_item._TicketTemplateMandatoryField is defined
and
template_item._TicketTemplateMandatoryField | length | int > 0
loop: "{{ template_item._TicketTemplateMandatoryField }}"
loop_control:
loop_var: sub_item
vars:
search_items:
- name: tickettemplates_id
value: "{{ tickettemplates_id }}"
- name: num
value: "{{ sub_item.num }}"
item:
api_path: TicketTemplate
sub_path: "{{ tickettemplates_id }}/TicketTemplateMandatoryField"
body:
num: "{{ sub_item.num }}"
tickettemplates_id: "{{ tickettemplates_id | int }}"
- name: Append/Create Ticket Template Predefined Fields
ansible.builtin.include_tasks:
file: api/append-create-item.yaml
when: >
template_item._TicketTemplatePredefinedField is defined
and
template_item._TicketTemplatePredefinedField | length | int > 0
loop: "{{ template_item._TicketTemplatePredefinedField }}"
loop_control:
loop_var: sub_item
vars:
search_items:
- name: tickettemplates_id
value: "{{ tickettemplates_id }}"
- name: num
value: "{{ sub_item.num }}"
item:
api_path: TicketTemplate
sub_path: "{{ tickettemplates_id }}/TicketTemplatePredefinedField"
body:
num: "{{ sub_item.num | int }}"
tickettemplates_id: "{{ tickettemplates_id | int }}"
value: "{{ sub_item.value | string }}"
- name: Append/Create Ticket Template Hidden Fields
ansible.builtin.include_tasks:
file: api/append-create-item.yaml
when: >
template_item._TicketTemplateHiddenField is defined
and
template_item._TicketTemplateHiddenField | length | int > 0
loop: "{{ template_item._TicketTemplateHiddenField }}"
loop_control:
loop_var: sub_item
vars:
search_items:
- name: tickettemplates_id
value: "{{ tickettemplates_id }}"
- name: num
value: "{{ sub_item.num }}"
item:
api_path: TicketTemplate
sub_path: "{{ tickettemplates_id }}/TicketTemplateHiddenField"
body:
num: "{{ sub_item.num | int }}"
tickettemplates_id: "{{ tickettemplates_id | int }}"
- name: Clear temp vars
ansible.builtin.set_fact:
template_item: {}
no_log: true

111
tasks/configure.yaml Normal file
View File

@ -0,0 +1,111 @@
---
- name: Clear Any downloaded files
ansible.builtin.file:
name: "/tmp/ansible"
state: absent
#loop: "{{ glpi_plugins }}"
- name: Clear Any downloaded files
ansible.builtin.file:
name: "/tmp/ansible"
state: directory
#loop: "{{ glpi_plugins }}"
# - name: Download Plugins
# ansible.builtin.get_url:
# url: "{{ item.url }}"
# dest: "/tmp/{{ item.filename }}"
# loop: "{{ glpi_plugins }}"
# - name: Download plugins
# ansible.builtin.command:
# cmd: "bash -c echo $({{ item.url }})"
# loop: "{{ glpi_plugins }}"
# wget https://github.com/pluginsGLPI/formcreator/releases/download/2.13.6/glpi-formcreator-2.13.6.tar.bz2 -O /tmp/plugin-form-creator.tar.bz2
# tar -xjf /tmp/plugin-form-creator.tar.bz2 -C /var/lib/docker/volumes/glpi_glpi_plugins/_data/
# wget https://github.com/glpi-project/glpi-inventory-plugin/releases/download/1.2.3/glpi-glpiinventory-1.2.3.tar.bz2 -O /tmp/glpi-glpiinventory-1.2.3.tar.bz2
# tar -xjf /tmp/glpi-glpiinventory-1.2.3.tar.bz2 -C /var/lib/docker/volumes/glpi_glpi_plugins/_data/
# chown www-data:www-data -R /var/lib/docker/volumes/glpi_glpi_plugins/_data/
- name: Install bzip
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop: "{{ apps }}"
vars:
apps:
- bzip2
- jq
- wget
# - name: Example clone of a single branch
# ansible.builtin.git:
# repo: "{{ item.repo }}.git"
# dest: "/var/lib/docker/volumes/plugins_glpi/_data/{{ item.name }}"
# single_branch: true
# version: "{{ item.version }}"
# depth: 1
# force: true
# loop: "{{ plugins }}"
# vars:
# plugins:
# - name: actualtime
# repo: https://github.com/ticgal/actualtime
# version: 2.1.0
# - name: glpiinventory
# repo: https://github.com/glpi-project/glpi-inventory-plugin
# version: 1.2.3
# # for creator doesnt work
# # - name: formcreator
# # repo: https://github.com/pluginsGLPI/formcreator
# # version: 2.13.6
# - name: geninventorynumber
# repo: https://github.com/pluginsGLPI/geninventorynumber
# version: 2.8.3
# - name: releases
# repo: https://github.com/InfotelGLPI/releases
# version: 2.0.3
# - name: phpsaml
# repo: https://github.com/DonutsNL/phpsaml
# # version: e7357a49ca9f0b612bc28879a0c703cdbfc4463b
# version: master
# - name: Download and Extract the plugins
# ansible.builtin.shell:
# cmd: |
# export URL=$(curl -s {{ item.url }} | jq .assets[0].browser_download_url | tr -d \");
# wget $URL -O /tmp/ansible/$(basename ${URL});
# tar -xjf /tmp/ansible/$(basename ${URL}) -C /var/lib/docker/volumes/plugins_glpi/_data/;
# loop: "{{ glpi_plugins }}"
# changed_when: false
- name: Fix file perms
ansible.builtin.command:
cmd: chown www-data:www-data -R /var/lib/docker/volumes/plugins_glpi/_data/
changed_when: false
# - name: Unarchive a file that needs to be downloaded (added in 2.0)
# ansible.builtin.unarchive:
# src: /tmp/{{ item.filename }}
# dest: /var/lib/docker/volumes/plugins_glpi/_data/
# remote_src: true
# extra_opts:
# - -j
# loop: "{{ glpi_plugins }}"
- name: Clear Any downloaded files
ansible.builtin.file:
name: "{{ item.filename }}"
state: absent
loop: "{{ glpi_plugins }}"
- name: Reload logroate if required
ansible.builtin.meta: flush_handlers

150
tasks/install.yaml Normal file
View File

@ -0,0 +1,150 @@
---
- name: GPLI Docker Container
ansible.builtin.include_role:
name: docker_management
vars:
docker_images:
- name: "{{ docker_image_name_glpi }}"
tag: "{{ docker_image_tag_glpi }}"
docker_networks:
- name: "{{ docker_container_name_glpi }}-access"
internal: false # this needs to be added to the docker role
- name: "{{ docker_container_name_glpi }}-smtp-access"
internal: true
docker_containers:
- name: "{{ docker_container_name_glpi }}"
image: "{{ docker_image_name_glpi }}:{{ docker_image_tag_glpi }}"
env:
TIMEZONE: UTC
networks:
- name: "{{ docker_container_name_glpi }}-access"
- name: "ingress-access"
- name: "{{ docker_container_name_glpi }}-smtp-access"
- name: ldap-access
- name: mysql-access
# published_ports:
# - 80:80
volumes:
- /usr/share/zoneinfo/Etc/UTC:/etc/timezone:ro
- /usr/share/zoneinfo/Etc/UTC:/etc/localtime:ro
- "config_{{ docker_container_name_glpi }}:/var/www/html/config"
- "data_{{ docker_container_name_glpi }}:/var/www/html/files"
- "log_{{ docker_container_name_glpi }}:/var/log"
- "marketplace_{{ docker_container_name_glpi }}:/var/www/html/marketplace"
- "plugins_{{ docker_container_name_glpi }}:/var/www/html/plugins"
docker_volumes:
- name: "plugins_{{ docker_container_name_glpi }}"
- name: "data_{{ docker_container_name_glpi }}"
- name: "config_{{ docker_container_name_glpi }}"
- name: "marketplace_{{ docker_container_name_glpi }}"
- name: "log_{{ docker_container_name_glpi }}"
- name: "varlog_{{ docker_container_name_glpi }}"
- name: Create GLPI database
community.mysql.mysql_db:
name: "{{ mysql_database_glpi }}"
state: present
login_unix_socket: "{{ mysql_unix_socket }}"
login_user: "{{ mysql_login_user }}"
login_password: "{{ mysql_login_password }}"
login_host: "{{ mysql_login_host | default('') }}"
config_file: ''
- name: Create user with password, all database privileges and 'WITH GRANT OPTION' in db1 and db2
community.mysql.mysql_user:
state: "{{ item.state | default('present') }}"
name: "{{ item.name }}"
password: "{{ item.password }}"
host: "{{ item.host | default('localhost') }}"
priv: "{{ item.priv | default(omit) | from_yaml }}"
update_password: "{{ item.update_password | default('on_create') }}"
login_host: "{{ mysql_login_host | default('') }}"
login_unix_socket: "{{ mysql_unix_socket }}"
login_user: "{{ mysql_login_user }}"
login_password: "{{ mysql_login_password }}"
config_file: ''
loop: "{{ database_mysql_users }}"
vars:
database_mysql_users:
- name: glpi
password: admin
host: '%'
priv:
'glpi.*': 'ALL,GRANT'
# sudo cp -r /var/www/html/glpi/config/* /var/lib/docker/volumes/glpi_config_glpi/_data/
# sudo cp -r /var/www/html/glpi/files/* /var/lib/docker/volumes/glpi_data_glpi/_data/
# sudo cp -r /var/www/html/glpi/plugins/* /var/lib/docker/volumes/glpi_glpi_plugins/_data/
# sudo cp -r /var/www/html/glpi/marketplace/* /var/lib/docker/volumes/glpi_marketplace_glpi/_data/
# sudo chmod -R 777 /var/lib/docker/volumes/glpi_config_glpi/_data/
# sudo chmod -R 777 /var/lib/docker/volumes/glpi_data_glpi/_data/
# sudo chmod -R 777 /var/lib/docker/volumes/glpi_glpi_plugins/_data/
# sudo chmod -R 777 /var/lib/docker/volumes/glpi_marketplace_glpi/_data/
- name: Add fail2ban filters
ansible.builtin.template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
mode: '760'
owner: root
group: root
loop: "{{ the_files }}"
# notify: reload_fail2ban
when: >
install_fail2ban | default(false) | bool
vars:
the_files:
- src: fail2ban.filter.conf
dest: "/etc/fail2ban/filter.d/glpi.local"
- src: fail2ban.filter.conf
dest: "/etc/fail2ban/filter.d/glpi-api.local"
- name: "Fail2Ban Jail for GLPI"
ansible.builtin.include_role:
name: nfc_firewall
when: >
install_fail2ban | default(false) | bool
vars:
fail2ban:
config:
- name: "glpi-{{ docker_container_name_glpi }}"
sub_path: jail.d
sections:
DEFAULT:
"glpi_log": "/var/lib/docker/volumes/data_{{ docker_container_name_glpi }}/_data/_log/event.log"
glpi:
enabled: true
mode: polling
chain: DOCKER-USER
port: http,https
logpath: "%(glpi_log)s"
filter: glpi
findtime: 600
maxretry: 5
- name: "api_glpi-{{ docker_container_name_glpi }}"
sub_path: jail.d
sections:
DEFAULT:
"api_glpi_log": "/var/lib/docker/volumes/log_{{ docker_container_name_glpi }}/_data/apache2/access-glpi.log"
api_glpi:
enabled: true
mode: polling
chain: DOCKER-USER
port: http,https
logpath: "%(api_glpi_log)s"
filter: glpi-api
findtime: 600
maxretry: 5
- name: Task Final playbook variables
ansible.builtin.set_fact:
glpi_installed: true

40
tasks/main.yaml Normal file
View File

@ -0,0 +1,40 @@
---
- name: Install GLPI
ansible.builtin.include_tasks:
file: install.yaml
apply:
tags:
- always
tags:
- always
when: >
install_glpi | bool
and
not glpi_installed | bool
- name: Configure Log Rotate
ansible.builtin.include_tasks:
file: configure.yaml
apply:
tags:
- always
tags:
- always
when: glpi_installed | bool
# and
# ansible_os_family == "Debian"
# and
# (
# logrotate is defined
# and
# logrotate | default([]) | length | int > 0
# )
- name: API tasks
ansible.builtin.include_tasks:
file: api/api.yaml
apply:
tags:
- always
when: glpi_config_as_code_json | length | int > 0

View File

@ -0,0 +1,15 @@
#
# Fail2ban Filter, GLPI API Denied access
#
# Managed By: Ansible
# Role: nfc_glpi
#
# Do not edit this file directly as it will be automagically updated by ansible.
# to make changes update the ansible play that deploys this file.
[Init]
maxlines = 2
[Definition]
failregex = ^(<HOST>[\d|\.?]{1,3}).+apirest.php.+"\s40\d\s\d{1,5}

View File

@ -0,0 +1,17 @@
#
# Fail2ban Filter, GLPI
#
# Managed By: Ansible
# Role: nfc_glpi
#
# Do not edit this file directly as it will be automagically updated by ansible.
# to make changes update the ansible play that deploys this file.
[Init]
maxlines = 2
[Definition]
# failregex = ^(?P<DATETIME>\d{4}-\d{2}-\d{2}\s?\d{2}:\d{2}:\d{2})\s+?\S+\n?\[login\]\s+?[\d+]?:?[\s+]?Failed\s+?login\s+?for\s(?P<USER>.+)\s+?from\s+?IP\s+?(?P<HOST>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$
# failregex = ^(.+)ogin] \d+: Failed login for (.+) from IP (<HOST>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$
failregex = ^.*?(Failed login for)[\S\s]+(from IP) <HOST>.*$
# ^.*\s(\[\S).*?(Failed login for) \S+ (from IP) <HOST>.*$

1
website-template Submodule

Submodule website-template added at 992b54805b