26 Sep 2016

AWS: Initialiser Oracle avec une Lambda NodeJS pour executer du SQL

Comment créer une ressource personnalisée pour CloudFormation qui utilise Lamdba et NodeJS pour injecter du SQL dans Oracle ?

1-hempleyrf2c0pcnvuoyonq

Rappel: CloudFormation c’est quoi ?

CloudFormation est le service d’Infrastructure as code d’Amazon Web Services pour automatiser la gestion des ressources.

On peut décrire les différents éléments d’infrastructure et applicatifs sous forme de documents qu’on injecte ensuite dans AWS qui se charge de créer, modifier, mettre à jour ou supprimer les ressources automatiquement. C’est la fin des erreurs humaines “oups, je me suis trompé de commande” et la fin des scripts de déploiement indéchiffrables!

Bon, Ce n’est pas réellement de l’infrastructure as code puisque le formalisme des templates est en JSON, c’est donc plutôt statique.

Étendre CloudFormation en fonction de ses besoins

Presque tous les services d’AWS sont gérés par CloudFormation mais il arrive que les nouveaux services ou les dernières fonctionnalités ne soient pas disponibles immédiatement.

Dans ce cas de figure, on peut se retrouver bloqué pour utiliser ses nouveautés avec CloudFormation. Mais ils sont trop forts chez Amazon, alors il existe la notion de Custom Resource qui sert à créer des ressources personnalisées.

On trouve de multiples exemple de ce type de ressource sur Github (ici ou ) et aussi des librairies et des services qui les utilisent pour ajouter des fonctionnalités sur AWS. Les articles de blog pullulent. A partir d’ici, mieux vaut avoir lu quelques articles sur CloudFormation et les ressources custom pour continuer.

Use case: Utiliser une Lambda NodeJS pour injecter une commande SQL dans Oracle

Si vous utilisez une base de données Oracle sur RDS, vous pouvez avoir besoin de l’initialiser en exécutant des requêtes SQL. Je n’aime pas trop faire ce genre de chose manuellement, alors voyons comment le faire automatiquement avec CloudFormation, Lambda, NodeJS et S3 !

Pour tester le concept, on va utiliser le service de stockage S3 pour y déposer un fichier SQL qui contient la commande à exécuter. Vous pouvez par exemple déposer ce fichier dans la même bucket S3 que celle où vous déposez vos sources packagées et votre configuration par exemple.

On va écrire une fonction Lambda qui prend en paramètre l’url du fichier SQL, les informations de connexion à la base Oracle pour télécharger le fichier, se connecter à la base et exécuter la requête. Si jamais ça fonctionne, il restera à mettre plusieurs lignes dans le fichier et à les parcourir pour les exécuter.

On souhaite utiliser NodeJS pour coder mais problème, le driver Oracle pour NodeJS est un peu disons capricieux: ce n’est pas un simple require qui va vous permettre de vous connecter à la base. Enfin si, mais jugez plutôt:

Grossomodo, le driver NodeJS natif pour Oracle ne fonctionne pas avec Lambda. Il faut utiliser un autre driver dans un autre langage. Mais il est possible de créer son propre driver en le forkant et en fixant certaines dépendances. Et une personne l’a donc fait et mis en open source:

Il est disponible sur npmjs et la construction de ce driver est détaillée dans un autre repo: une VM EC2 est utilisée pour récupérer toutes les dépendances nécessaires et pour construire une version du driver pour Lambda. L’utilisation d’un driver pour une base de données doit se faire en toute confiance, il est important de s’assurer que le code du driver est sûr. Dans le cas d’un driver custom, il est important de construire sa propre version du driver. Voila le détail de la génération du driver:

Une fois qu’on a ce driver, l’utilisation d’Oracle avec NodeJS sur Lambda se fait simplement.

Le code de la Custom Resource Lambda NodeJS Oracle

Il faut quelques lignes de code pour récupérer le fichier SQL sur S3, l’exécuter en se connectant à la base Oracle et envoyer le résultat à CloudFormation.

La particularité de cette lambda est que les paramètres envoyés par CloudFormation sont disponibles dans ‘event.ResourceProperties’ par convention et que pour retourner une réponse en fin de traitement il faut utiliser le module ‘cfn-response’ d’AWS:

Quand tout va bien on retourne:

response.send(event, context, response.SUCCESS, myResponse))

Et en cas de problème, on renvoie:

response.send(event, context, response.FAILED, {Error: 'TooManyErrorsException'})

Il est important de retourner une réponse quoi qu’il arrive, sinon CloudFormation va attendre des lustres que la Lambda réponde lors de exécution de la Stack.

Pour déployer cette Lambda on va utiliser CloudFormation en définissant un template qui crée la fameuse ressource custom. La Lambda doit avoir les droits pour lire dans S3, se connecter à la base, écrire des logs, etc…

Voici le template d’exemple: https://github.com/clakech/lambda-oracle-sql-injector/blob/master/template.json

La déclaration de la Lambda est la même que pour une Lambda classique avec CloudFormation, mais là on va également définir la ressource Custom qui lui fait référence en passant les paramètres username, password, chaîne de connexion Oracle, bucket S3 du fichier SQL et son nom:

On trouve également un fichier params.json pour renseigner les différents paramètres: https://github.com/clakech/lambda-oracle-sql-injector/blob/master/params.json

Le zip du code de lambda doit avoir été déposé dans S3 au préalable:

Notre fichier Sample.sql d’exemple est très simple et contient ce select: SELECT table_name FROM user_tables

Pour finir, il reste à créer la Stack pour tester:

Voila pour ce POC ; reste à bien ficeler les droits d’utilisation des différents services pour limiter les accès au strict nécessaire.

On peut envisager d’exécuter des commandes pour créer des comptes ou des tables Oracle par exemple. Et il faut faire évoluer le code pour lire chacune des lignes dans le fichier SQL.

Une des limites de l’usage des Lambda dans ce cas, c’est son temps d’exécution qui est de 5 minutes maximum. Alors si vous avez des scripts SQL trop longs à s’exécuter, il reste plusieurs options; réduire ces scripts, découper les scripts et exécuter plusieurs Lambda, ou bien utiliser une VM avec EC2 pour faire l’équivalent.

PS: Abonnez vous au RSS du blog, retrouvez nous sur twitter avec le hashtag #AxaWebCenter et suivez moi sur @cyril_lakech, linkedin ou medium 😉

Share