Mon voyage dans Terraform : Partie 1

Chez D2SI nous croyons à l’automatisation d’infrastructures. Et en tant que DevOps, dans ce contexte d’automatisation, notre mission est de chercher sans relâche les meilleurs outils, et parfois de parier sur ces outils. Terraform, parmi tant d’autres, fait partie de ces outils.

Terraform est un outil open source permettant de builder, modifier et versionner une infrastructure de façon fiable et automatisée. Il permet aux Ops de bénéficier de l’IaC (Infrastructure as Code) pour décrire des infrastructures basées sur le langage déclaratif HCL (HashiCorp Configuration Language). Voici un exemple qui sera interprété en un appel d’API  pour créer une instance AWS (ou machine virtuelle) de type t2.nano (500MB RAM, 1vCPU) et basée sur l’AMI ID ami-e5083683 (Amazon Linux 2017.03.0) :

# main.tf
resource "aws_instance" "example" {
  ami           = "ami-e5083683"
  instance_type = "t2.nano"
}

En utilisant la ligne de commande, vous pouvez interagir avec cette configuration et appliquer vos changements. Le voyage peut commencer !

Aller plus loin

Avec mon background de développeur et travaillant actuellement sur des missions Ops, Terraform m’apporte la logique et la flexibilité nécessaire pour construire les infrastructures de mes clients : provisionnement des VPC (Virtual Private Cloud), gestion de load balancers, de bases de données, déploiements d’applications Serverless, etc. Il y a quelques mois de cela, j’avais un besoin de traitement de données brutes puis de conversion pour un format structuré pour des fins d’affichage. Il fallait délivrer un message de notification à une WebApp utilisant un endpoint HTTPS protégé par mot de passe, de type : https://username:password@example.com/myroute.

L’outil utilisé dans ce but s’appelle SNS : Simple Notification Service. C’est un service managé d’AWS permettant aux utilisateurs d’envoyer des notifications vers de multiples endpoints en utilisant un protocole email, HTTP, SMS, etc. Pour ce faire, il faut d’abord créer un topic, puis ajouter les souscriptions (qui doivent être confirmées). Si on utilise la ligne de commande AWS, cela ressemble à :

aws sns subscribe --topic-arn arn:aws:sns:eu-west-1:123456789098:my-topic --protocol http --notification-endpoint https://username:password@example.com/myroute

Cela créerait une souscription au endpoint https://username:password@example.com/myroute, lié au topic arn:aws:sns:eu-west-1:123456789098:my-topic utilisant le protocole http.

Cette documentation précise de façon explicite que le endpoint doit auto-confirmer l’URL unique envoyée par AWS, en y faisant un appel. Cependant, bien que la confirmation ait été faite côté AWS et que la console l’ait exposée avec succès, l’abstraction Terraform n’en était pas consciente, ce qui créait un timeout.

Réparer la matrice

Je me suis alors dit que corriger ce bug serait pour moi une opportunité parfaite de contribuer au projet Terraform. J’ai alors commencé à m’intéresser au code et à me familiariser avec le langage de programmation Go. En progressant doucement dans le code, j’ai commencé à documenter ce que j’avais trouvé, et à explorer ce qui était déjà en cours dans les différents pull requests.

Une fois que j’ai eu gagné en confiance, et afin de trouver ce bug, je suis entré dans le process de développement Terraform. Une fois passé l’étape des pré-requis, j’ai commencé ma session de debug :

TF_LOG=DEBUG TF_LOG_PATH=terraform.log terraform apply

Voici quelques éléments pour expliquer ce qui est fait :

  • TF_LOG=DEBUG : définit le niveau de log sur DEBUG, ce qui permet de tracer presque tout.
  • TF_LOG_PATH : loggue tout dans le terraform.log file

En prenant l’exemple précédent, c’est-à-dire https://username:password@example.com/myroute, j’ai découvert que le endpoint retourné par AWS en souscrivant contenait une chaîne de caractères masqués (obfuscated) , remplaçant le champ de mot de passe. Ce qui signifie qu’en envoyant :

https://username:password@example.com/myroute

On obtient en retour :

https://username:****@example.com/myroute

Cela m’a amené au bug, qui comparait la valeur envoyée (mon endpoint) avec celle retournée (masquée), et donc générait une erreur. Donc j’ai crée une petite fonction reproduisant le masquage (obfuscation) en utilisant Go :

// returns the endpoint with obfuscated password, if any
func obfuscateEndpointPassword(endpoint string) string {
    r := regexp.MustCompile("(://[^:]+):([^@]+)")
    return r.ReplaceAllString(endpoint, fmt.Sprintf("$1:%s", awsSNSPasswordObfuscationPattern))
}

Ce simple morceau de code traduit n’importe quelle URL en une version avec masquage :

 

URL Obfuscated version
“https://example.com/myroute” “https://example.com/myroute”
“https://username@example.com/myroute” “https://username@example.com/myroute”
“https://username:password@example.com/myroute” “https://username:**@example.com/myroute”

Une fois le code écrit et les tests unitaires passés, j’ai corrigé l’instruction responsable du bug et effectué ma première contribution à Terraform. Tout est sur Github et devrait être mergé prochainement !

Le voyage n’est pas terminé

Je dois avouer que le développeur en moi est tombé amoureux de Go et de l’API Terraform. Ce travail était ma première expérience avec la base de code de Terraform, et sous la bannière de D2SI, j’ai depuis contribué à de nombreuses corrections de documentation, de corrections de bugs et j’ai ainsi pu aider la communauté. Mon voyage dans le monde Terraform ne fait que commencer !

Version anglaise de cet article sur le Tech Blog D2SI

Commentaires :

A lire également sur le sujet :