Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1168cf62f3 | |||
| 73a94c3c50 | |||
| 8e698cfc49 | |||
| e25463ff89 | |||
| 064ecfad43 | |||
| 13bc1f46d7 |
31
README.md
31
README.md
@ -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
|
||||
|
||||
28
compose.yml
28
compose.yml
@ -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"
|
||||
10
dockerfile
10
dockerfile
@ -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"]
|
||||
26
pre-start.py
26
pre-start.py
@ -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}")
|
||||
|
||||
|
||||
34
scripts/StagingUserAssignation.py
Executable file → Normal file
34
scripts/StagingUserAssignation.py
Executable file → Normal 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
|
||||
|
||||
@ -67,13 +67,21 @@ if(args.staginguser != None):
|
||||
stagingUser = args.staginguser
|
||||
|
||||
# ====================================== #
|
||||
|
||||
|
||||
|
||||
# Vérification de la présence du verrou avant de continuer
|
||||
if(os.path.isfile(lockFile) and not args.force):
|
||||
logger.debug('Lock file exists, exiting...')
|
||||
exit(0)
|
||||
# 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
|
||||
|
||||
1
scripts/functions.py
Executable file → Normal file
1
scripts/functions.py
Executable file → Normal 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)
|
||||
|
||||
33
scripts/includes/GLPIAPI.py
Executable file → Normal file
33
scripts/includes/GLPIAPI.py
Executable file → Normal file
@ -110,12 +110,23 @@ 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,10 +151,28 @@ 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")
|
||||
inventory.SetOperatingSystem(platformName, device.OS, osArch)
|
||||
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)
|
||||
|
||||
|
||||
0
scripts/includes/__init__.py
Executable file → Normal file
0
scripts/includes/__init__.py
Executable file → Normal file
0
scripts/includes/airwatchAPI.py
Executable file → Normal file
0
scripts/includes/airwatchAPI.py
Executable file → Normal file
29
scripts/syncGLPI.py
Executable file → Normal file
29
scripts/syncGLPI.py
Executable file → Normal 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):
|
||||
logger.debug('Lock file exists, exiting...')
|
||||
exit(0)
|
||||
# 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:
|
||||
|
||||
39
start.sh
39
start.sh
@ -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
|
||||
Reference in New Issue
Block a user