Compare commits

..

6 Commits

11 changed files with 77 additions and 154 deletions

View File

@ -3,7 +3,7 @@
## Explication de l'usage des scripts et de la configuration :
### Fichier de configuration global settings.json
Les scripts prennent les informations de configuration du fichier de configuration présent dans le répertoire conf, si celui-ci n'existe pas au lancement d'un script, il est automatiquement créé avec des valeurs d'exemples.
Les scripts prennent les informations de configuration du fichier de configuration présent dans le répertoire conf, si celui-ci n'existe pas au lancement d'un script, il est automatiquement créé avec des valeurs d'exemples. Un chemin personnalisé vers un fichier de configuration peut être renseigné avec le paramètre -c.
Voici un exemple du fichier de configuration :
@ -85,10 +85,6 @@ Il récupère le nom de l'utilisateur de staging dans le fichier de configuratio
- **-s / --silent** : exécute le script sans faire de retour dans la console, les informations seront toujours présentes dans les fichiers de log
- **-v / --verbose** : affiche des informations lors de son exécution, utile pour résoudre des problèmes liés à des droits d'accès API ou des problèmes d'ouvertures réseaux
## Installation avec docker
## Dépendances
Installation des paquets linux :
@ -102,28 +98,9 @@ apt-get install -y python3 python3-pip git
yum install -y python3 python3-pip git
```
## Modules python nécessaires
Voici la liste des modules python nécessaires au fonctionnement des scripts :
- cryptography
- requests
- toml
Un fichier requirements.txt contenant les modules dont dépendent les scripts est présent dans le répertoire scripts.
Les modules peuvent être installés de plusieurs façons :
**Avec le gestionnaire de paquet python Pip**
**Dépendances python**
```
python -m pip install -r requirements.txt
```
**Avec le gestionnaire de paquet apt (debian/ubuntu)**
```
apt-get install -y python3-cryptography python3-requests python3-toml
```
**Avec le gestionnaire de paquet yum (redhat/centos)**
```
yum install -y python3-cryptography python3-requests python3-toml
python3 -m pip install cryptography requests toml
```
## Installation des scripts
@ -195,7 +172,7 @@ Description=Script qui assigne les appareils en staging aux utilisateurs en se b
[Timer]
OnUnitInactiveSec=1m
AccuracySec=1us
Unit=airwatchStaging.service
Unit=airwatchSync.service
[Install]
WantedBy=timers.target

View File

@ -1,28 +0,0 @@
services:
sync:
image: emitlinks/airwatchConnector
restart: unless-stopped
volumes:
- ./conf:/airwatchConnector/conf
- ./certs:/airwatchConnector/certs
- ./logs:/airwatchConnector/logs
healthcheck:
test: if [ -f $(ls /airwatchConnector/*_SyncGLPI.lock) ]; then exit 0; else exit 1; fi
interval: 5m
start_period: 3h
environment:
TASK: "syncGLPI"
stagingAssignment:
image: emitlinks/airwatchConnector
restart: unless-stopped
volumes:
- ./conf:/airwatchConnector/conf
- ./certs:/airwatchConnector/certs
- ./logs:/airwatchConnector/logs
healthcheck:
test: if [ -f $(ls /airwatchConnector/*_StagingUserAssignation.lock) ]; then exit 0; else exit 1; fi
interval: 15s
start_period: 5m
environment:
TASK: "stagingAssignment"

View File

@ -1,10 +0,0 @@
FROM python
RUN <<-EOF
apt-get update
apt-get install -y python3-cryptography python3-requests python3-toml
EOF
ADD scripts\* /airwatchConnector
ADD pre-start.py /
ADD start.sh /
RUN chmod u+x /start.sh
ENTRYPOINT ["/start.sh"]

View File

@ -1,26 +0,0 @@
import os
import toml
baseDir = "/airwatchConnector"
logDir = f"{baseDir}/logs"
confDir = f"{baseDir}/conf"
confFiles = os.listdir(confDir)
# On récupère que les fichiers .conf
confFiles = [conf for conf in confFiles if os.path.isfile(f"{confDir}/{conf}") and not conf.endswith(".conf")]
for conf in confFiles:
# On forme un nom à partir du nom du fichier de conf sans l'extension
# et on enlève les espaces
confName = conf[:5].replace(' ', '')
with open(f"{confDir}/{conf}", "r") as f:
settings = toml.load(f)
# Create log folder
if(settings["LOGS"]["Enabled"]):
logPath = settings["LOGS"].get(Path)
if not os.path.exists(f"{logDir}/{logPath}"):
os.makedirs(f"{logDir}/{logPath}")

View File

@ -2,6 +2,7 @@
import os
import argparse
import logging
import time
from functions import getSettings
from includes.airwatchAPI import *
from includes.GLPIAPI import *
@ -20,7 +21,7 @@ args = parser.parse_args()
if(args.configpath != None and args.configpath != ''):
settings = getSettings(args.configpath)
else:
settings = getSettings(f"{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
settings = getSettings("./conf/settings.conf")
#=========== Configuration des logs ===========#
@ -41,7 +42,7 @@ if(settings["LOGS"]["Enabled"]):
if(settings["LOGS"].get("Path") and settings["LOGS"].get("Path") != ""):
fileHandler = logging.FileHandler(f"{settings['LOGS'].get('Path')}stagingUserAssignation.log")
else:
fileHandler = logging.FileHandler(f'{os.path.realpath(os.path.dirname(__file__))}/logs/stagingUserAssignation.log')
fileHandler = logging.FileHandler('./logs/stagingUserAssignation.log')
fileHandler.setLevel(logginglevel)
fileHandler.setFormatter(formatter)
logger.addHandler(fileHandler)
@ -56,8 +57,7 @@ if(not args.silent):
#======== Paramètres du script ========#
# Emplacement du verrou
nameForLockFile = settings["GLPI"]["UserAgent"].replace(' ', '-')
lockFile = f'{os.path.realpath(os.path.dirname(__file__))}/{nameForLockFile}_StagingUserAssignation.lock'
lockFile = './airwatchStagingUserAssignation.lock'
debug=args.debug
@ -68,12 +68,20 @@ if(args.staginguser != None):
# ====================================== #
# Vérification de la présence du verrou avant de continuer
if(os.path.isfile(lockFile) and not args.force):
# Récupération du temps de création de verrou en minutes
lockTime = (time.time() - os.path.getmtime(lockFile)) // 60
# Recréation du verrou s'il existe depuis plus de 3 heures (crash)
# sinon on quitte, une synchro est déjà en cours
if(lockTime > 180):
os.remove(lockFile)
open(lockFile, "w").close()
else:
logger.debug('Lock file exists, exiting...')
exit(0)
else:
# Création du verrou s'il n'existe pas
open(lockFile, "w").close()
# Initialisation de l'api Airwatch
@ -143,15 +151,9 @@ for device in devices:
else:
logger.warning(f"Device with id {device.Id} is not assigned to any user in GLPI, skipping the device")
elif(deviceCount > 1):
logger.error(f"{count} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI ids = {', '.join(deviceID)}), skipping this device...")
logger.info(f"More than one entry found in GLPI for device with id {device.Id}")
else:
deviceIDTrash, dataTrash, deviceCountTrash = glpiapi.GetDevice(device, trashbin=True)
if(countTrash > 1):
logger.error(f"{countTrash} devices matching airwatch device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI ids = {', '.join(deviceIDTrash)}), skipping this device...")
elif(countTrash == 1):
logger.warning(f"Device {device.FriendlyName} (Airwatch id={device.Id}) in GLPI trashbin (GLPI id={deviceIDTrash}), skipping...")
else:
logger.error(f"Device {device.FriendlyName} (Airwatch id={device.Id}) not found in GLPI.")
logger.error(f"Device {device.Id} with serialnumber {device.SerialNumber} not found in GLPI (in trash bin ?)")
# Suppression du verrou

View File

@ -5,6 +5,7 @@ def getSettings(settingsPath):
settingsDefault ="""
[AIRWATCH]
Server = "https://airwatchServer"
ConsoleURI = "https://airwatchConsole"
APIKey = "APIKEY"
# Méthode d'authentification (CMSURL or PASSWORD)

View File

@ -111,11 +111,22 @@ class GLPIAPI:
uri = f"{self.Server}/apirest.php/Computer/"
return requests.put(uri, headers=self.Headers, json=body)
def UpdateAirwatchLink(self, deviceid, airwatchlink):
body = {
"input": {
"id": deviceid,
"appareilsurmagentafield": airwatchlink
}
}
uri = f"{self.Server}/apirest.php/Computer/"
return requests.put(uri, headers=self.Headers, json=body)
def CreateInventoryForAirwatchDevice(self, device, deviceName, apps=None):
platforms = {
2:"Apple iOS",
5:"Android",
12:"Windows Desktop"
12:"Windows"
}
if(device.PlatformId in platforms.keys()):
@ -140,9 +151,27 @@ class GLPIAPI:
osArch = "Unknown"
softwareArch = "Unknown"
windowsOSTranslation = {
"10.0.19043":"10 21H1",
"10.0.19044":"10 21H2",
"10.0.19045":"10 22H2",
"10.0.22000":"11 21H2",
"10.0.22621":"11 22H2",
"10.0.22631":"11 23H2",
"10.0.26100":"11 24H2",
"10.0.26200":"11 25H2"
}
logDate = datetime.strptime(device.LastSeen, "%Y-%m-%dT%H:%M:%S.%f").strftime("%Y-%m-%d %H:%M:%S")
inventory = GLPIInventory(logdate=logDate, versionclient=self.UserAgent, tag=device.Group, deviceid=f"{deviceName} - {device.SerialNumber}", itemtype="Computer")
if(platformName == "Windows"):
if(device.OS in windowsOSTranslation.keys()):
inventory.SetOperatingSystem(platformName, windowsOSTranslation[str(device.OS)], osArch)
else:
platformName = "Windows Desktop"
inventory.SetOperatingSystem(platformName, device.OS, osArch)
else:
inventory.SetOperatingSystem(platformName, device.OS, osArch)
inventory.SetHardware(deviceName, device.Uuid, device.TotalMemory)
inventory.AddUser(device.User)

View File

@ -2,6 +2,7 @@
import os
import argparse
import logging
import time
from datetime import datetime
from functions import getSettings
from includes.airwatchAPI import *
@ -22,7 +23,7 @@ args = parser.parse_args()
if(args.configpath != None and args.configpath != ''):
settings = getSettings(args.configpath)
else:
settings = getSettings("{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
settings = getSettings(f"{os.path.realpath(os.path.dirname(__file__))}/conf/settings.conf")
#=========== Configuration des logs ===========#
@ -105,11 +106,21 @@ platformFilterOut = [12]
# ====================================== #
# Vérification de la présence du verrou avant de continuer
if(os.path.isfile(lockFile) and not args.force):
# Récupération du temps de création de verrou en minutes
lockTime = (time.time() - os.path.getmtime(lockFile)) // 60
# Recréation du verrou s'il existe depuis plus de 3 heures (crash)
# sinon on quitte, une synchro est déjà en cours
if(lockTime > 180):
os.remove(lockFile)
open(lockFile, "w").close()
else:
logger.debug('Lock file exists, exiting...')
exit(0)
else:
# Création du verrou s'il n'existe pas
open(lockFile, "w").close()
logger.info("========= Synchronization started =========")
@ -216,11 +227,17 @@ for device in devices:
inventory = glpiapi.CreateInventoryForAirwatchDevice(device, data["1"], apps)
# Mise à jour du friendly name sur Airwatch
platformName = inventory.operatingsystem["name"]
if(device.FriendlyName != f"{data['1']} {platformName} {device.OS} - {device.User}"):
newFriendlyName = f"{data['1']} {platformName} {device.OS} - {device.User}"
osVersion = inventory.operatingsystem["version"]
if(device.FriendlyName != f"{data['1']} {platformName} {osVersion} - {device.User}"):
newFriendlyName = f"{data['1']} {platformName} {osVersion} - {device.User}"
logger.info(f"Updating device friendlyname to {newFriendlyName}")
airwatch.SetDeviceFriendlyName(device, newFriendlyName)
# Mise à jour de l'url vers la page airwatch de l'appareil sur GLPI
airwatchlink = f"{settings['AIRWATCH']['ConsoleURI']}/AirWatch/#/AirWatch/Device/Details/Summary/{device.Id}"
if(data['76689'] != airwatchlink):
glpiapi.UpdateAirwatchLink(deviceID, airwatchlink)
# filtre des plateformes
if(platformFilterEnabled):
if device.PlatformId in platformFilterOut:

View File

@ -1,39 +0,0 @@
#!/bin/bash
python /pre-start.py
if [ $CONF != '' ]; then
case $TASK in
syncGLPI)
python3 /airwatchConnector/sync.py -c $CONF -f
;;
stagingAssignment)
python3 /airwatchConnector/stagingUserAssignation.py -c $CONF -f
;;
*)
exit 0
;;
esac
else
confFiles = $(ls /airwatchConnector/conf/*.conf)
if [ $confFiles = '' ]; then exit 1; do
for CONF in $confFiles; do
case $TASK in
syncGLPI)
python3 /airwatchConnector/sync.py -c $CONF -for
;;
stagingAssignment)
python3 /airwatchConnector/stagingUserAssignation.py -c $CONF -f
;;
*)
exit 0
;;
esac
done
fi