Terraform AWS Cloud : gestion saine de l'infrastructure

Publié: 2022-03-11

La rédaction d'une application n'est qu'une partie de l'histoire. Pour qu'il soit utile, il doit être déployé à un endroit où il peut évoluer, il doit fonctionner avec une haute disponibilité, il a besoin de sauvegardes, etc.

De plus en plus de développeurs doivent au moins maîtriser ce processus de déploiement. Cela se manifeste, par exemple, dans le fait que DevOps devient un rôle souvent demandé de nos jours, à mesure que la complexité du système augmente. Nous ne pouvons pas nous permettre d'ignorer ces changements et devons être conscients de la façon de concevoir des applications pour qu'elles soient facilement déployables.

C'est également dans l'intérêt de nos clients : ils nous embauchent en tant qu'experts dans nos domaines et attendent de nous que nous leur fournissions l'intégralité du produit, souvent du début à la fin. Ils ont des exigences et sont souvent inconscients de la pile sur laquelle s'exécute leur solution d'entreprise. En fin de compte, c'est la valeur commerciale du produit qui compte.

Présentation de Terraform

Le déploiement et la gestion de l'infrastructure ne sont pas un processus simple. En plus d'une expertise de domaine en constante évolution, nous devons apprendre un autre outil ou un nouveau flux de travail. Si vous avez reporté cela, cet article est une chance pour vous de vous familiariser avec une approche de la gestion des infrastructures. J'espère qu'à la fin vous serez plus confiant dans l'utilisation de Terraform et que vous en saurez plus sur les approches et les défis possibles. Vous devriez pouvoir utiliser cet outil pour au moins commencer à gérer une partie de votre infrastructure cloud.

Terraform est une couche d'abstraction, oui, et les abstractions fuient, je suis d'accord. Mais en fin de compte, nous sommes dans le domaine de la résolution de problèmes et de la gestion des abstractions. Celui-ci vise à nous fournir plus de bon sens dans nos tâches quotidiennes.

Le but

J'expliquerai ce qu'est Terraform, comment il s'intègre à l'ensemble de l'écosystème et comment il se compare à d'autres outils similaires. Ensuite, je vous montrerai les étapes nécessaires pour configurer une configuration Terraform multi-environnement et prête pour la production pour une équipe. J'expliquerai les bases de l'écriture de la configuration de Terraform : comment gérer la complexité et dupliquer le code avec des modules partageables.

Les exemples seront tous axés sur un seul fournisseur de cloud : Amazon Web Services (AWS). C'est juste un nuage avec lequel j'ai le plus d'expérience, mais toutes les informations devraient également s'appliquer à d'autres nuages.

Je terminerai par quelques notes que j'aurais aimé connaître quand j'ai commencé : quelques problèmes de syntaxe, des bizarreries et des cas où Terraform ne serait pas mon outil de prédilection.

Je ne me concentrerai pas sur les détails de la syntaxe. Vous pouvez vous familiariser rapidement avec cela en lisant la documentation et les guides fantastiques fournis par HashiCorp pour Terraform. J'aimerais me concentrer sur des choses qui ne sont peut-être pas évidentes dès le début et que j'aurais aimé savoir avant de commencer à travailler avec Terraform. Espérons que cela vous orientera dans la bonne direction et vous permettra de considérer Terraform comme un outil à utiliser dans de futurs projets.

La gestion de l'infrastructure est difficile

La configuration d'un environnement pour une application dans le cloud comporte plusieurs étapes. À moins que vous ne les écriviez tous sous forme de listes de contrôle détaillées et que vous ne les suiviez de près, tout le temps, vous ferez des erreurs ; nous sommes humains, après tout. Ces étapes sont difficiles à partager. Vous devez documenter de nombreuses procédures manuelles et les documents peuvent rapidement devenir obsolètes. Multipliez maintenant tout cela par le nombre total d'environnements pour une seule application : dev , test/qa , stage et prod . Vous devez également penser à la sécurité de chacun d'entre eux.

Chaque outil n'a-t-il pas une interface utilisateur que je peux utiliser et oublier la complexité ?

Ils ont des interfaces utilisateur - AWS Management Console en est un excellent exemple. Mais ces outils font beaucoup sous le capot. Un clic sur l'interface utilisateur peut en fait invoquer une cascade de changements difficiles à saisir. Il n'y a généralement aucun moyen d'annuler ce que vous avez fait dans l'interface utilisateur (les invites habituelles « Êtes-vous sûr ? » ne suffisent souvent pas). De plus, c'est toujours une bonne idée d'avoir une deuxième paire d'yeux pour vérifier les modifications, mais lorsque nous utilisons des interfaces utilisateur, nous devons nous asseoir avec cette personne ensemble ou vérifier nos modifications après qu'elles ont été apportées, ce qui est plus un audit qu'un la revue. Chaque fournisseur de cloud a sa propre interface utilisateur que vous devez maîtriser. Les interfaces utilisateur sont conçues pour être faciles à utiliser (pas nécessairement simples !) et, en tant que telles, sont sujettes à des approches délirantes telles que "ce n'est qu'un petit ajustement" ou un correctif de production rapide que vous oublierez en 48 heures. Un tel clic manuel est également très difficile à automatiser.

Qu'en est-il des outils CLI ?

Ils seraient meilleurs que les outils d'interface utilisateur pour nos cas d'utilisation. Cependant, vous êtes toujours enclin à effectuer des modifications à la main ou à écrire des scripts bash qui peuvent facilement devenir incontrôlables. De plus, chaque fournisseur a ses propres outils CLI. Avec ces outils, vous ne pouvez pas voir vos modifications avant de les valider. Heureusement, ce n'est pas un problème nouveau et il existe des outils qui aident à résoudre ce problème.

Orchestration vs gestion de la configuration

Je définirai certaines catégories d'outils et de pratiques qui sont utilisées pour gérer l'infrastructure. Il s'agit de l'orchestration et de la gestion de la configuration. Vous pouvez les considérer à peu près comme des modèles de programmation déclaratifs et impératifs (pensez Prolog vs Python). Chacun a ses propres avantages et inconvénients, mais il est préférable de tous les connaître et d'appliquer le meilleur outil pour un travail donné. De plus, l'orchestration implique généralement un niveau d'abstraction plus élevé que la gestion de la configuration.

Orchestration

L'orchestration ressemble plus à un paradigme de programmation déclarative. J'aime penser à lui comme étant le chef d'orchestre. Le travail d'un chef d'orchestre est joliment résumé par Wikipédia (lien) comme "l'art de diriger la performance simultanée de plusieurs joueurs ou chanteurs par l'utilisation du geste". Les chefs d'orchestre communiquent le rythme et le tempo, la dynamique et le repérage de la musique, mais le travail proprement dit est exécuté par des musiciens individuels qui sont des experts pour jouer de leurs instruments de musique. Sans une telle coordination, ils ne pourraient pas exécuter une pièce parfaite.

C'est là que Terraform s'inscrit. Vous l'utilisez pour gérer une partie de l'infrastructure informatique : vous lui dites ce qu'il faut déployer, et Terraform relie le tout et effectue tous les appels d'API nécessaires. Il existe des outils similaires dans cet espace; l'un des plus connus est AWS Cloud Formation. Il a un meilleur support pour la récupération et les retours en arrière en cas d'erreur que Terraform, mais aussi, à mon avis, une courbe d'apprentissage plus abrupte. Il n'est pas non plus indépendant du cloud : il ne fonctionne qu'avec AWS.

Gestion de la configuration

Le versant complémentaire de ces pratiques est l'approche de gestion de configuration. Dans ce paradigme, vous spécifiez les étapes exactes qu'un outil doit effectuer pour arriver à une configuration donnée et souhaitée. Les étapes elles-mêmes peuvent être petites et prendre en charge plusieurs systèmes d'exploitation, mais vous devez réfléchir activement à l'ordre de leur exécution. Ils n'ont aucune conscience de l'état actuel et de l'environnement (à moins que vous ne les programmiez avec cela) et, en tant que tels, exécuteront aveuglément toutes les étapes que vous leur donnerez. Cela peut entraîner un problème connu sous le nom de dérive de configuration , où vos ressources se désynchroniseront lentement avec ce qu'elles étaient censées représenter initialement, surtout si vous y avez apporté des modifications manuelles. Ils sont excellents pour gérer et fournir des services sur des instances individuelles. Des exemples d'outils qui excellent dans ce flux de travail sont Chef, Puppet, Ansible et Salt.

L'orchestration impose une approche de votre infrastructure dans laquelle vous traitez vos ressources comme du bétail et non comme des animaux de compagnie. Au lieu de « nourrir » manuellement chaque VPS, vous pouvez les remplacer par une copie exacte en cas de problème. Je ne veux pas dire que vous vous en fichez tout simplement et que vous redémarrez la chose en espérant le meilleur.

Meme de l'émission télévisée IT Crowd avec leur slogan emblématique : avez-vous essayé de l'éteindre et de le rallumer ?

Au lieu de cela, vous devez rechercher et résoudre le problème dans le code , puis le déployer.

Ansible (et d'autres outils CM) peuvent être utilisés pour gérer l'infrastructure AWS, mais cela impliquerait beaucoup de travail et est plus sujet aux erreurs, en particulier lorsque l'infrastructure change souvent et devient de plus en plus complexe.

Une chose importante à retenir est que les approches d'orchestration et de gestion de la configuration ne sont pas en conflit les unes avec les autres. Ils sont compatibles. Il est parfaitement acceptable d'avoir un groupe d'instances EC2 (VPS) dans un groupe AutoScaling géré par Terraform mais exécutant une image d'application AWS (AMI), qui est un instantané du disque, qui a été préparé avec des étapes impératives avec, par exemple, Ansible . Terraform a même un concept de "fournisseurs" qui vous permet d'exécuter des outils de provisionnement externes une fois qu'une machine démarre.

La documentation Terraform fait un excellent travail pour expliquer cela plus en détail et vous aider à placer Terraform dans l'ensemble de l'écosystème.

Qu'est-ce que Terraform ?

C'est un outil open source, créé par HashiCorp qui vous permet de codifier votre infrastructure sous forme de fichiers de configuration déclaratifs qui sont versionnés et partagés et qui peuvent être révisés.

Le nom HashiCorp devrait sonner une cloche - ils font aussi Nomad, Vault, Packer, Vagrant et Consul. Si vous avez utilisé l'un de ces outils, vous connaissez déjà la qualité de la documentation, la communauté dynamique et l'utilité que vous pouvez attendre de leurs solutions.

Infrastructure en tant que code

Terraform est indépendant de la plate-forme ; vous pouvez l'utiliser pour gérer des serveurs bare metal ou des serveurs cloud comme AWS, Google Cloud Platform, OpenStack et Azure. Dans le jargon Terraform, ceux-ci sont appelés fournisseurs . Vous pouvez vous faire une idée de l'échelle en lisant une liste complète des fournisseurs pris en charge. Plusieurs fournisseurs peuvent être utilisés en même temps, par exemple lorsque le fournisseur A configure les machines virtuelles pour vous, mais que le fournisseur B configure et délègue les enregistrements DNS.

Cela signifie-t-il que l'on peut changer de fournisseur de cloud avec une seule modification dans un fichier de configuration ? Non, je ne pense même pas que vous voudriez cela, du moins pas de manière automatisée. Le problème est que différents fournisseurs peuvent avoir des capacités différentes, des offres, des flux, des idées, etc. différents. Cela signifie que vous devrez utiliser différentes ressources pour qu'un fournisseur différent exprime le même concept. Cependant, tout cela peut toujours être fait dans une syntaxe de configuration unique et familière et faire partie d'un flux de travail cohérent.

Éléments essentiels d'une configuration Terraform

  1. Le binaire Terraform lui-même, que vous devez installer
  2. Les fichiers de code source, c'est-à-dire votre configuration
  3. L'état (local ou distant) qui représente les ressources gérées par Terraform (plus à ce sujet plus tard)

Écriture de la configuration de Terraform

Vous écrivez le code de configuration Terraform dans des fichiers *.tf à l'aide du langage HCL. Il existe une option pour utiliser le format JSON ( *.tf.json ), mais il est destiné aux machines et à la génération automatique plutôt qu'aux humains. Je vous recommande de vous en tenir à HCL. Je ne vais pas plonger profondément dans la syntaxe du langage HCL ; la documentation officielle fait un travail fantastique en décrivant comment écrire HCL et comment utiliser les variables et les interpolations. Je ne mentionnerai que le strict minimum nécessaire pour comprendre les exemples.

Dans les fichiers Terraform, vous traitez principalement des ressources et des sources de données . Les ressources représentent des composants de votre infrastructure, par exemple, une instance AWS EC2, une instance RDS, un enregistrement DNS Route53 ou une règle dans un groupe de sécurité. Ils vous permettent de les provisionner et de les modifier à l'intérieur de l'architecture cloud.

En supposant que vous avez configuré Terraform, si vous émettez un terraform apply , le code ci-dessous créera une instance EC2 entièrement fonctionnelle (seules certaines propriétés sont affichées) :

 resource "aws_instance" "bastion" { ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2 instance_type = "t2.nano" key_name = "${var.key_name}" subnet_ vpc_security_group_ids = ["${aws_security_group.bastion.id}"] monitoring = "false" associate_public_ip_address = "true" disable_api_termination = "true" tags = { Name = "${var.project_tag}-bastion-${var.env}" Env = "${var.env}" Application ApplicationRole = "Bastion Host" Project = "${var.project_tag}" } }

D'autre part, il existe des sources de données qui vous permettent de lire des données sur des composants donnés sans les modifier. Vous souhaitez obtenir l'ID AWS (ARN) d'un certificat émis par ACM ? Vous utilisez une source de données. La différence est que les sources de données sont préfixées par data_ lors de leur référencement dans les fichiers de configuration.

 data "aws_acm_certificate" "ssl_cert" { domain = "*.example.com" statuses = ["ISSUED"] }

Ce qui précède fait référence à un certificat SSL ACM émis qui peut être utilisé avec les ALB AWS. Avant de faire tout cela, vous devez configurer votre environnement.

Structure des dossiers

Les environnements Terraform (et leurs états) sont séparés par des répertoires. Terraform charge tous les fichiers *.tf d'un répertoire dans un espace de noms, donc l'ordre n'a pas d'importance. Je recommande la structure de répertoire suivante :

 /terraform/ |---> default_variables.tf (1) /stage/ (2) |---> terraform.tfvars (3) |---> default_variables.tf (4) |---> terraform.tf (5) |---> env_variables.tf (6) /prod/ /<env_name>/
  1. default_variables.tf – définit toutes les variables de niveau supérieur et éventuellement leurs valeurs par défaut. Ils peuvent être réutilisés dans chaque environnement (répertoire imbriqué) avec des liens symboliques.
  2. /stage/ – un répertoire contenant la configuration d'un environnement entièrement séparé (ici nommé stage , mais cela peut être n'importe quoi). Toutes les modifications apportées à l'intérieur de ce dossier sont totalement indépendantes des autres environnements (env—comme prod ) ce que vous voulez afin d'éviter de gâcher l'environnement de production avec les modifications apportées à stage !
  3. terraform.tfvars – définit les valeurs des variables. Les fichiers .tfvars sont similaires aux fichiers .env en ce sens qu'ils contiennent les paires key=val pour les variables définies. Par exemple, cela spécifie le profile AWS, AWS key_name et AWS key_path que j'utilise. Il peut être ignoré dans Git.
  4. default_variables.tf - c'est un lien symbolique vers le fichier (2), qui nous permet de partager des variables indépendantes de l'environnement sans nous répéter.
  5. terraform.tf - il s'agit de la configuration principale de chaque environnement ; il contient le bloc terraform {} qui configure le back-end. Je configure également les fournisseurs ici.
  6. env_variables.tf - ce fichier contient des variables spécifiques à env. Je marque toutes les ressources avec Env=<env_name> dans AWS, donc ce fichier ne définit généralement qu'une seule variable : env .

Bien sûr, ce n'est pas la seule façon de structurer votre environnement. C'est juste quelque chose qui a bien fonctionné pour moi en permettant une séparation claire des préoccupations.

Configuration dorsale

J'ai déjà mentionné l'état de Terraform. Il s'agit d'une partie essentielle du flux de travail Terraform. Vous vous demandez peut-être si l'état est réellement requis. Terraform ne pourrait-il pas simplement interroger l'API AWS en permanence pour obtenir l'état réel de l'infrastructure ? Eh bien, si vous y réfléchissez, Terraform doit maintenir un mappage entre ce qu'il gère dans les fichiers de configuration déclaratifs et ce à quoi ces fichiers correspondent réellement (dans l'environnement du fournisseur de cloud). Notez que, lors de l'écriture des fichiers de configuration Terraform, vous ne vous souciez pas des ID, par exemple, des instances EC2 individuelles ou des ARN qui seront créés pour les groupes de sécurité que vous publiez. En interne, cependant, Terraform doit savoir qu'un bloc de ressources donné représente une ressource concrète avec un ID/ARN. Ceci est nécessaire pour détecter les changements. De plus, l'état est utilisé pour suivre les dépendances entre les ressources (ce à quoi vous n'avez généralement pas à penser !). Ils sont utilisés pour construire un graphe qui peut être (généralement) parallélisé et exécuté. Comme toujours, je vous recommande de lire l'excellente documentation sur l'état de Terraform et son objectif.

Étant donné que l'état est la seule source de vérité pour votre architecture, vous devez vous assurer que vous et votre équipe travaillez toujours sur sa version la plus à jour et que vous ne créez pas de conflits par un accès non synchronisé à l'état. Vous ne voulez pas résoudre les conflits de fusion sur le fichier d'état, croyez-moi.

Par défaut, Terraform stocke l'état dans un fichier sur le disque, situé dans le répertoire de travail actuel (de chaque env) sous la forme d'un fichier terraform.tfstate . Ce n'est pas grave si vous savez que vous serez le seul développeur au travail ou que vous apprenez et expérimentez simplement Terraform. Techniquement, vous pouvez le faire fonctionner en équipe car vous pouvez valider l'état dans un référentiel VCS. Mais alors, vous devrez vous assurer que tout le monde travaille toujours sur la dernière version de l'état et que personne ne change en même temps ! C'est généralement un gros mal de tête et je le déconseille fortement. De plus, si quelqu'un rejoint votre opération de développement unique, vous devrez toujours configurer un autre emplacement pour l'état.

Heureusement, c'est un problème avec une bonne solution intégrée à Terraform : le soi-disant Remote State . Pour que l'état distant fonctionne, vous devez configurer le back-end à l'aide de l'un des fournisseurs de back-end disponibles. L'exemple de back-end suivant sera basé sur AWS S3 et AWS DynamoDB (base de données AWS NoSQL). Vous ne pouvez utiliser que S3, mais vous perdez alors le mécanisme de verrouillage d'état et de vérification de cohérence (non recommandé). Si vous n'utilisiez auparavant que l'état local, la configuration d'un back-end distant vous offrira la possibilité de migrer votre état la première fois, afin de ne rien perdre. Vous pouvez en savoir plus sur la configuration back-end ici.

Malheureusement, il y a un problème de poule et d'œuf : le compartiment S3 et la table DynamoDB doivent être créés manuellement. Terraform ne peut pas les créer automatiquement car il n'y a pas encore d'état ! Eh bien, il existe des solutions comme https://github.com/gruntwork-io/terragrunt qui automatisent cela à l'aide de l'AWS CLI, mais je ne veux pas m'écarter du sujet principal de cet article de blog.

Les choses importantes à savoir sur la configuration du backend S3 et DynamoDB sont :

  1. Activez la gestion des versions sur le compartiment S3 pour vous protéger des erreurs humaines et de la loi de Murphy.
  2. La table DynamoDB a une limite de débit sur les lectures et les écritures (appelée capacité). Si vous apportez de nombreuses modifications à l'état distant, assurez-vous d'activer DynamoDB AutoScaling pour cette table ou de configurer des limites R/W suffisamment élevées. Sinon, Terraform obtiendra des erreurs HTTP 400 de l'API AWS lors de l'exécution d'un grand nombre d'appels.

Pour résumer, la configuration principale suivante peut être placée dans terraform.tf pour configurer l'état distant sur S3 et DynamoDB.

 terraform { # Sometimes you may want to require a certain version of Terraform required_version = ">= 0.11.7" # Stores remote state, required for distributed teams # Bucket & dynamoDB table have to be created manually if they do not exist # See: https://github.com/hashicorp/terraform/issues/12780 backend "s3" { bucket = "my-company-terraform-state" key = "app-name/stage" region = "eu-west-1" # 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table. # See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html dynamodb_table = "terraform-state-lock-table" } }

C'est beaucoup à assimiler à la fois, mais rappelez-vous, vous faites cela une fois pour chaque env et vous pouvez ensuite l'oublier. Si vous avez besoin d'encore plus de contrôle sur le verrouillage d'état, il y a HashiCorp Terraform Enterprise, mais je ne le couvrirai pas ici.

Fournisseurs

Pour que ce back-end soit accessible et puisse communiquer avec notre fournisseur de cloud, nous devons configurer le soi-disant fournisseur . Le bloc suivant peut être placé dans le fichier terraform.tf (pour chaque env) :

 provider "aws" { profile = "${var.profile}" region = "${var.region}" version = "~> 1.23.0" }

Les variables de profile et de region sont stockées dans le fichier terraform.tfvars , qui peut être ignoré. La variable de profile fait référence à un profil nommé contenant les informations d'identification de sécurité pour le cloud AWS à l'aide du fichier d'informations d'identification standard. Notez que je définis également certaines contraintes de version. Vous ne voulez pas que Terraform mette à niveau les plugins de votre fournisseur à votre insu à chaque initialisation back-end. D'autant plus qu'il existe une version 2.x du fournisseur AWS qui nécessite une mise à niveau minutieuse.

Initialisation du back-end

Chaque configuration back-end nécessite une étape d'initialisation au début et à chaque fois qu'une modification y est apportée. L'initialisation configure également les modules Terraform (plus d'informations sur ceux-ci plus tard), donc lorsque vous les ajoutez, vous devez également réexécuter l'étape d'initialisation. Cette opération peut être exécutée plusieurs fois en toute sécurité. Notez que, lors de l'initialisation du back-end, toutes les variables ne peuvent pas être lues par Terraform pour configurer l'état, ni pour des raisons de sécurité (par exemple, les clés secrètes). Pour surmonter cela et, dans notre cas, utiliser un profil AWS différent de celui par défaut, vous pouvez utiliser l'option -backend-config avec accepts k=v paires de variables. C'est ce qu'on appelle la configuration partielle.

 terraform init -backend-config=profile=<aws_profile_name>

Les fichiers Terraform partagent une étendue limitée à un répertoire donné. Cela signifie qu'un sous-dossier n'est pas directement connecté au code du répertoire parent. Il s'agit cependant de définir un module qui permet la réutilisation du code, la gestion de la complexité et le partage.

Le flux de travail

Le flux de travail général lorsque vous travaillez avec du code Terraform est le suivant :

  1. Écrivez la configuration de votre infrastructure.
  2. Voyez quels changements réels il apportera ( terraform plan ).
  3. Éventuellement, exécutez les modifications exactes que vous avez vues à l'étape 2 ( terraform apply ).
  4. ALLER À 1

Plan de Terraform

La commande plan Terraform vous présentera une liste des modifications qui seront apportées à votre infrastructure lors de l'émission de la commande apply . Il est prudent d'émettre un plan plusieurs fois, car cela ne change rien en soi.

Comment lire un plan

Les objets dans Terraform (ressources et sources de données) sont facilement identifiables par leurs noms complets.

  1. Dans le cas des ressources, l'ID peut ressembler à : <resource_type>.<resource_name> —par exemple, aws_ecs_service.this .
  2. Dans le cas des ressources à l'intérieur des modules, nous avons un nom de module supplémentaire : module.<module_name>.<resource_type>.<resource_name> — par exemple, module.my_service_ecs_service_task.aws_ecs_service.this .
  3. Sources de données (à l'intérieur et à l'extérieur d'un module) : (module.<module_name>).data.<resource_type>.<resource_name> —par exemple, module.my_service_ecs_service_task.data.aws_ecs_task_definition.this .

Le type de ressource est spécifique à un fournisseur donné et inclut généralement son nom ( aws_… ). La liste complète des ressources disponibles pour AWS se trouve dans la documentation.

Il y a cinq actions qu'un plan montrera pour des ressources données :

  1. [+] Ajouter – Une nouvelle ressource sera créée.
  2. [-] Détruire - Une ressource sera complètement détruite.
  3. [~] Modifier sur place - Une ressource sera modifiée et un ou plusieurs paramètres seront modifiés. Ceci est généralement sans danger. Terraform vous montrera quels paramètres seront modifiés et comment (si possible).
  4. [- / +] – La ressource sera supprimée puis recréée avec de nouveaux paramètres. Cela se produit si une modification a été apportée à un paramètre qui ne peut pas être modifié sur place. Terraform vous montrera quelles modifications forcent la recréation d'une ressource avec le commentaire suivant en rouge : (forces new resource) . Ceci est potentiellement dangereux, car il y a une période pendant laquelle la ressource n'existera pas du tout. Il peut également briser d'autres dépendances connectées. Je recommanderais de contourner ces changements, sauf si vous savez quelles en seront les conséquences ou si vous ne vous souciez pas d'un temps d'arrêt.
  5. [<=] – Une source de données sera lue. Il s'agit d'une opération en lecture seule.

Terraform AWS — un exemple de "plan"

Ci-dessus, un exemple de plan. Les modifications que j'ai apportées étaient :

  • Modification de l' instance_type de la première instance de bastion
  • Ajout d'une deuxième instance de bastion
  • Modification du nom d'un groupe de sécurité

Notez que la dernière modification est une règle de groupe de sécurité qui a automatiquement détecté une modification du nom du groupe parent. À mon avis, l'ensemble du plan est très lisible. Certaines valeurs de paramètre s'affichent sous la forme <computed> — cela ne signifie pas qu'elles seront modifiées, mais plutôt qu'elles ne peuvent pas être récupérées et présentées à ce stade (comme le nom du SG qui n'a pas encore été créé). N'oubliez pas que seules les ressources modifiées seront affichées lors de la commande plan . Tout ce qui est omis ne sera pas touché.

Généralement, les commandes Terraform acceptent des paramètres supplémentaires. Le paramètre le plus important de la commande plan est l'option -out , qui enregistrera votre plan sur le disque. Ce plan enregistré peut ensuite être exécuté (exactement tel qu'il a été enregistré) par la commande apply. Ceci est très important, car sinon il pourrait y avoir un changement, effectué par exemple par des collègues entre votre émission d'un plan et l'émission d' apply . Exemple:

  1. Émettez le plan et vérifiez qu'il a l'air bien.
  2. Quelqu'un change l'état, à votre insu.
  3. Problème d' apply et - oups, il a fait autre chose que ce qui était prévu !

Heureusement, ce flux de travail a été amélioré dans Terraform v0.11.0. Depuis cette version, la commande apply vous présente automatiquement un plan que vous devez ensuite approuver (en tapant explicitement yes ). L'avantage est que, si vous appliquez ce plan, il sera exécuté exactement comme présenté.

Une autre option utile est -destroy , qui vous montrera le plan qui entraînera la destruction de toutes les ressources gérées par Terraform. Vous pouvez également cibler des ressources spécifiques à détruire avec l'option -target .

Appliquer

Lorsque nous appliquons un plan donné, Terraform sort et met un verrou sur notre état pour assurer l'exclusivité. Il procède ensuite au changement des ressources et, à la fin, pousse un état mis à jour. Une chose à noter est que certaines ressources prennent plus de temps à terminer que d'autres. Par exemple, la création d'une instance AWS RDS peut prendre plus de 12 minutes et Terraform attendra que cela se termine. De toute évidence, Terraform est suffisamment intelligent pour ne pas bloquer toutes les autres opérations. il crée un graphe orienté des modifications demandées et, s'il n'y a pas d'interdépendances, utilise le parallélisme pour accélérer l'exécution.

Importation de ressources

Souvent, Terraform et d'autres solutions de « configuration en tant que code » sont introduites progressivement, dans un environnement déjà existant. La transition est vraiment facile. Fondamentalement, tout ce qui n'est pas défini dans Terraform est laissé non géré et inchangé. Terraform ne se préoccupe que de ce qu'il gère. Bien sûr, il est possible que cela présente des problèmes, par exemple si Terraform s'appuie sur un point de terminaison qui existe en dehors de sa configuration et qu'il est ensuite détruit manuellement. Terraform ne le sait pas et ne peut donc pas recréer cette ressource lors des changements d'état, ce qui entraînera des erreurs de l'API lors de l'exécution du plan. Ce n'est pas quelque chose dont je m'inquiéterais; les avantages de l'introduction de Terraform l'emportent largement sur les inconvénients.

Afin de faciliter un peu le processus d'introduction, Terraform inclut la commande import . Il est utilisé pour introduire des ressources externes déjà existantes dans l'état de Terraform et lui permettre de gérer ces ressources. Malheureusement, Terraform n'est pas en mesure de générer automatiquement des configurations pour ces modules importés, du moins au moment de la rédaction. Il existe des plans pour cette fonctionnalité, mais pour l'instant, vous devez d'abord écrire la définition de la ressource dans Terraform, puis importer cette ressource pour indiquer à Terraform de commencer à la gérer. Une fois importé dans l'état (et lors de la planification de l'exécution), vous verrez toutes les différences entre ce que vous avez écrit dans les fichiers .tf et ce qui existe réellement dans le cloud. De cette façon, vous pouvez encore peaufiner la configuration. Idéalement, aucun changement ne devrait apparaître, ce qui signifierait que la configuration de Terraform reflète ce qui est déjà sur le cloud 1:1.

Modules

Les modules sont une partie essentielle de la configuration de Terraform, et je vous recommande de les adopter et de les utiliser fréquemment. Ils vous permettent de réutiliser certains composants. Un exemple serait le cluster AWS ECS, qui est utilisé pour exécuter des conteneurs Docker. Pour qu'un tel cluster fonctionne, vous avez besoin de nombreuses ressources distinctes à configurer : le cluster lui-même, la configuration de lancement qui gérerait des instances EC2 distinctes, les référentiels de conteneurs pour les images, le groupe et la stratégie de mise à l'échelle automatique, etc. Vous avez généralement besoin de clusters distincts pour des environnements et/ou des applications distincts.

Une façon de surmonter cela serait de copier et coller la configuration, mais c'est évidemment une solution à courte vue. Vous feriez des erreurs en faisant les mises à jour les plus simples.

Les modules vous permettent d'encapsuler toutes ces ressources distinctes dans un seul bloc de configuration (appelé module). Les modules définissent des entrées et des sorties qui sont les interfaces par lesquelles il communique avec « le monde extérieur » (ou le code appelant). De plus, les modules peuvent être imbriqués dans d'autres modules, ce qui vous permet de créer rapidement des environnements entièrement séparés.

Modules locaux et distants

De même pour déclarer, vous pouvez avoir des modules locaux ou des modules distants. Les modules locaux sont stockés avec votre configuration Terraform (dans un répertoire séparé, en dehors de chaque environnement mais dans le même référentiel). C'est OK si vous avez une architecture simple et que vous ne partagez pas ces modules.

Ceci, cependant, a des limites. Il est difficile de versionner ces modules et de les partager. La gestion des versions est importante, car vous souhaiterez peut-être utiliser la version 1.0.0 de votre module ECS en production, mais souhaitez expérimenter la version 1.1.0 dans un environnement intermédiaire. Si le module était stocké à côté de votre code, chaque modification du code du module serait reflétée dans chaque env (une fois l' apply exécutée), ce qui n'est généralement pas souhaitable.

Une approche pratique pour gérer les versions des modules consiste à les placer tous dans un référentiel séparé, par exemple, your-company/terraform-modules. Ensuite, lorsque vous référencez ces modules dans votre configuration Terraform, vous pouvez utiliser un lien VCS comme source :

 module "my-module" { source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0" ... }

Ici, je fais référence à une v1.1.0 de my-module (chemin spécifique) que je peux tester indépendamment des autres versions du même module dans différents environnements.

En dehors de cela, il y a la question de la découvrabilité et de la partageabilité des modules. Vous devez vous efforcer d'écrire des modules bien documentés et réutilisables. Habituellement, vous aurez différentes configurations Terraform pour différentes applications et vous souhaiterez peut-être partager le même module entre elles. Sans les extraire dans un dépôt séparé, ce serait très difficile.

Utiliser des modules

Les modules peuvent être facilement référencés dans les environnements Terraform en définissant un bloc de module spécial. Voici un exemple d'un tel bloc pour un module ECS hypothétique :

 module "my_service_ecs_cluster" { source = "../modules/ecs_cluster" cluster_name = "my-ecs-service-${var.env}" repository_names = [ "my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ] service_name = "my-ecs-service-${var.env}" ecs_instance_type = "t2.nano" min_size = "1" max_size = "1" use_autoscaling = false alb_target_group_arn = "${module.my_alb.target_group_arn}" subnets = "${local.my_private_subnets}" security_groups = "${aws_security_group.my_ecs.id}" key_name = "${var.key_name}" env_tag = "${var.env}" project_tag = "${var.project_tag}" application_tag = "${var.api_app_tag}" asg_tag = "${var.api_app_tag}-asg" }

Toutes les options passées (à l'exception de certaines options globales comme source ) ont été définies dans la configuration de ce module en tant qu'entrées (variables).

Registre des modules

Récemment, HashiCorp a lancé un registre officiel de modules Terraform. C'est une excellente nouvelle puisque vous pouvez désormais puiser dans les connaissances de la communauté qui a déjà développé des modules testés au combat. De plus, certains d'entre eux ont le badge "HashiCorp Verified Module", ce qui signifie qu'ils sont contrôlés et activement entretenus et vous donne une confiance supplémentaire.

Auparavant, vous deviez soit écrire vos propres modules à partir de zéro (et apprendre de vos erreurs), soit utiliser des modules publiés sur GitHub et d'autres endroits, sans aucune garantie quant à leur comportement (à part la lecture du code !)

Partage de données entre environnements

Idéalement, les environnements doivent être totalement séparés, même en utilisant différents comptes AWS. En réalité, il existe des cas où un environnement Terraform peut utiliser certaines informations dans un autre environnement. This is especially true if you are gradually converting your architecture to use Terraform. One example might be that you have a global env that provides certain resources to other envs.

Let's say env global shares data with stage . For this to work, you can define outputs at the main level of the environment like so:

 output "vpc_id" { value = "${module.network.vpc_id}" }

Then, in the stage environment, you define a datasource that points to the remote state of global :

 data "terraform_remote_state" "global" { backend = "s3" config { bucket = "my-app-terraform-state" key = "terraform/global" region = "${var.region}" dynamodb_table = "terraform-state-lock-table" profile = "${var.profile}" } }

Now, you can use this datasource as any other and access all the values that were defined in global 's outputs:

 vpc_

Words of Caution

Terraform has a lot of pros. I use it daily in production environments and consider it stable enough for such work. Having said that, Terraform is still under active development. Thus, you will stumble on bugs and quirks.

Where to Report Issues and Monitor Changes

First of all, remember: Terraform has a separate core repo and repositories for each provider (eg, AWS). If you encounter issues, make sure to check both the core repo and the separate provider repositories for issues and/or opened pull requests with fixes. GitHub is really the best place to search for bugs and fixes as it is very active and welcoming.

This also means that provider plugins are versioned separately, so make sure you follow their changelogs as well as the core one. Most of the bugs I have encountered were resolved by upgrading the AWS provider which already had a fix.

Can't Cheat Your Way out of Cloud Knowledge

You cannot use Terraform to configure and manage infrastructure if you have no knowledge of how a given provider works. I would say this is a misconception and not a downside, since Terraform has been designed to augment and improve the workflow of configuration management and not to be some magic dust that you randomly sprinkle around and—poof! Environments grow! You still need a solid knowledge of a security model of each cloud, how to write, eg, AWS policies, what resources are available, and how they interact.

Prefer Separate Resources That Are Explicitly linked

There are certain resources—for example, the AWS security group or AWS route table—that allow you to configure the security rules and routes respectively, directly inside their own block. This is tempting, as it looks like less work but in fact will cause you trouble. The problems start when you are changing those rules on subsequent passes. The whole resource will be marked as being changed even if only one route/security rule is being introduced. It also gives implicit ordering to those rules and makes it harder to follow the changes. Thankfully, mixing those both approaches is not allowed now (see the note).

Best-practice example, with explicitly linked resources:

 resource "aws_security_group" "my_sg" { name = "${var.app_tag}-my-sg" ... } resource "aws_security_group_rule" "rule_one" { security_group_ ... } resource "aws_security_group_rule" "rule_two" { security_group_ ... }

Terraform plan Doesn't Always Detect Issues and Conflicts

I already mentioned this in the case where you were managing resources with Terraform that were relying on other, unmanaged infrastructure. But there are more trivial examples—for example, you will get an error if your EC2 instance has Termination Protection enabled, even though plan would show you it's OK to destroy it. You can argue that this is what Termination Protection has been designed for, and I agree, but there are more examples of things you can do in theory/on plan but when executed will deadlock or error out. For example, you cannot remove a network interface if something is using it—you get a deadlock without an option to gracefully recover.

Syntax Quirks

There are also quirks related to how HCLv1 (the syntax language Terraform uses) has been designed. It has a couple of frustrating quirks. There is work underway to provide an improved version of the parser for HCLv2. The best way to read on the current limitations and the plan to overcome them is this fantastic blog series. In the meantime, there are workarounds for most of those issues. They are not pretty and they will fail once v0.12 comes out, but hey, it is what it is.

When State Update Fails

It sometimes happens that Terraform is not able to correctly push an updated state. This is usually due to underlying network issues. The solution is to retry the state update instead of running apply again, which will fork the state .

Another issue might happen when state lock (the synchronization primitive that prevents multiple users to update the same state) fails to be taken down by Terraform. This involves running terraform force-unlock with the lock ID to take it down manually.

Thankfully, in case of such problems, Terraform provides you with a good description and steps you need to make to fix it.

Not Everything Is Fun to Manage Through Terraform

There are certain cases where Terraform is not my tool of choice. For example, configuring AWS CodePipeline and CodeBuild projects (AWS equivalent of CI/CD pipeline) is cumbersome when done through Terraform. You need to define each step through very verbose configuration blocks and things like “Login via GitHub” are a lot more complicated than using the UI. Of course, it's still possible if you prefer to have it codified. Well, I guess it's a good candidate for a well-written module!

Same thing goes for managing AWS API Gateway endpoints. In this case, using a dedicated serverless framework would be a better option.

When configuring AWS resources with Terraform, you will find yourself writing a lot of policies. Policies that would otherwise often be auto-generated for you (when using the UI). For those, I'd recommend the AWS Visual Editor and then copying the resulting policy JSON into Terraform.

Conclusion

Using Terraform has been fun and I'll continue doing so. Initial steps can be a bumpy ride, but there are more and more resources that help to ease you in.

I'd definitely recommend taking Terraform for a spin and simply playing with it. Remember, though—be safe and test it out on a non-essential account. If you are eligible for AWS Free Tier, use it as a 12-month free trial. Just be aware it has limitations as to what you can provision. Otherwise, just make sure you spin the cheapest resources, like t3.nano instances.

I highly recommend extensions for Terraform support in various code editors. For Visual Studio Code, there is one with syntax highlighting, formatting, validation and linting support.

It's always valuable to learn new things and evaluate new tools. I found that Terraform helped me immensely in managing my infrastructure. I think working with Terraform will only get easier and more fun, especially once v0.12.0 ships with a major upgrade to the HCL syntax and solve most of the quirks. The traction and community around Terraform are active and vibrant. You can find a lot of great resources on things I didn't manage to cover in a single blogs post, eg, a detailed guide on how to write modules.

Related: Terraform vs. CloudFormation: The Definitive Guide