Table of Contents

WebAPI

Faisons un quick win. On souhaite réaliser un POC 1) à propos d'une requête et pour cela on va réaliser une API Web RestFul.

Requête

Tous les laptops Windows de l'entreprise sont supposés exécuter un script tous les jours. Cependant on a une tonne de tickets qui peuvent être expliqués par le fait que le script n'est pas exécuté ou bien qu'il lève une sorte de timeout.

L'idée est de logguer quelque part le moment où le script commence et finit. L'idée est bonne mais on n'a aucune idée de commencer centraliser les logs de tous les laptops. Alors pourquoi pas une API Web RestFul :-) ?

Avant d'aller plus loin: oui je sais qu'il existe une multitude de solutions à ce problème mais la cible est surtout de faire un POC en moins d'une heure. Comme j'avais l'habitude de faire des API, c'est mon choix :-).

Architecture

Architecture WebAPI

Source DIA

Côté client

From client side, we are going to use Powershell command as it is native to Windows 10.

Du côté client, on va utiliser un script Powershell comme c'est natif sur Windows 10.

client_side.ps1
# init
$who=whoami
$computer=hostname
$server="my-server.domain.com"
 
# before the main command, let's call the API
Invoke-WebRequest ("http://{2}:5000/my_command/start/{0}/{1}" -f ($who -replace "\\", "@"), $computer, $server) -Method POST
 
# main command
my_action
 
# after the main command, let's call the API
Invoke-WebRequest ("http://{2}:5000/my_command/end/{0}/{1}" -f ($who -replace "\\", "@"), $computer, $server) -Method POST

L'API retourne quelque chose comme cela:

StatusCode        : 201
StatusDescription : Created
Content           : {"user": "my_user", "action": "start", "ip_remote": "0.1.2.3", "computer":
                    "my_computer"}

RawContent        : HTTP/1.1 201 Created
                    Connection: close
                    Content-Length: 115
                    Content-Type: application/json
                    Date: Tue, 26 May 2020 13:46:28 GMT
                    Server: gunicorn/20.0.4

                    {"user": "my_user", "action": "star...
Forms             : {}
Headers           : {[Connection, close], [Content-Length, 115], [Content-Type, application/json], [Date, Tue, 26 May 2020 13:46:28
                    GMT]...}
Images            : {}
InputFields       : {}
Links             : {}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 115

Si on voulait adapter le script sur un environnement Linux, on pourrait partir sur quelque chose comme cela:

curl -i -XPOST --silent http://localhost:5000/my_command/start/toto/computer
HTTP/1.1 201 CREATED
Server: gunicorn
Date: Fri, 16 Apr 2021 03:07:27 GMT
Connection: close
Content-Type: application/json
Content-Length: 86
 
{"user": "toto", "action": "start", "ip_remote": "127.0.0.1", "computer": "computer"}

Côté Serveur

Lancement

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
gunicorn  --bind 0.0.0.0:5000 wsgi:app

Logger

logger.py
import logging
 
logger = logging.getLogger('api')
logger.setLevel(logging.DEBUG)
# create file handler which logs even debug messages
fh = logging.FileHandler('api.log')
fh.setLevel(logging.INFO)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter and add it to the handlers
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
ch.setFormatter(formatter)
fh.setFormatter(formatter)
# add the handlers to logger
logger.addHandler(ch)
logger.addHandler(fh)

WSGI

wsgi.py
from api import app
 
if __name__ == "__main__":
    app.run()

API

api.py
from flask import Flask, request
from flask_restful import Resource, Api
from logger import logger
 
 
app = Flask(__name__)
api = Api(app)
 
 
class HelloWorld(Resource):
    def post(self, user, action, computer):
        logger.info(f"{computer},{action},{user},{request.remote_addr}")
        return {
            "user": user,
            "action": action,
            "ip_remote": request.remote_addr,
            "computer": computer
        }
 
 
api.add_resource(
    HelloWorld,
    '/my_command/<string:action>/<string:user>/<string:computer>'
)
 
if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")

Requirements

requirements.txt
flake8
flask_restful
gunicorn
1)
Preuve de concept