Que sont les modules Terraform et comment fonctionnent-ils?

Étonnamment, beaucoup de débutants ignorent les modules Terraform par souci de simplicité, du moins c'est ce qu'ils pensent.

Plus tard, ils se retrouvent à parcourir des centaines de lignes de code de configuration.

Je suppose que vous connaissez déjà certaines des bases de Terraform et que vous avez même essayé de l'utiliser auparavant. Sinon, consultez cette présentation sur Terraform et ce didacticiel vidéo avant de continuer la lecture.

Remarque: je n'utilise pas intentionnellement de vrais exemples de code avec un fournisseur spécifique comme AWS ou Google, juste pour des raisons de simplicité.

Modules Terraform

Vous écrivez déjà des modules

Même lorsque vous ne créez pas un module intentionnellement, si vous utilisez Terraform, vous écrivez déjà un module - un module dit " racine ".

Tout fichier de configuration Terraform ( .tf) dans un répertoire, même un seul, forme un module.

Que fait un module?

Un module Terraform vous permet de créer une abstraction logique au-dessus d'un ensemble de ressources. En d'autres termes, un module vous permet de regrouper des ressources et de réutiliser ce groupe plus tard, éventuellement plusieurs fois.

Supposons que nous ayons un serveur virtuel avec certaines fonctionnalités hébergées dans le cloud. Quel ensemble de ressources pourrait décrire ce serveur? Par exemple:

  • la machine virtuelle elle-même, créée à partir d'une image
  • un périphérique bloc attaché d'une taille spécifiée pour un stockage supplémentaire
  • une adresse IP publique statique mappée à l'interface réseau virtuelle du serveur
  • un ensemble de règles de pare-feu à attacher au serveur
  • d'autres choses comme un autre périphérique bloc, une interface réseau supplémentaire, etc.

Supposons maintenant que vous deviez créer ce serveur avec un ensemble de ressources plusieurs fois. C'est là que les modules sont vraiment utiles - vous ne voulez pas répéter le même code de configuration encore et encore, n'est-ce pas?

Voici un exemple qui illustre comment notre module "serveur" pourrait être appelé.

" Appeler un module " signifie l'utiliser dans le fichier de configuration.

Ici, nous créons 5 instances du "serveur" en utilisant un seul ensemble de configurations (dans le module):

module "server" { count = 5 source = "./module_server" some_variable = some_value }

Organisation du module: enfant et racine

Bien sûr, vous voudrez probablement créer plus d'un module. Voici quelques exemples courants:

  • un réseau comme un cloud privé virtuel (VPC)
  • hébergement de contenu statique (c.-à-d. buckets)
  • un équilibreur de charge et ses ressources associées
  • une configuration de journalisation
  • ou tout ce que vous considérez comme un composant logique distinct de l'infrastructure

Disons que nous avons deux modules différents: un module "serveur" et un module "réseau". Le module appelé «réseau» est l'endroit où nous définissons et configurons notre réseau virtuel et y plaçons des serveurs:

module "server" { source = "./module_server" some_variable = some_value } module "network" { source = "./module_network" some_other_variable = some_other_value }

Une fois que nous avons des modules personnalisés, nous pouvons les appeler des modules "enfants". Et le fichier de configuration où nous appelons les modules enfants se rapporte au module racine.

Un module enfant peut provenir d'un certain nombre d'endroits:

  • chemins locaux
  • le registre officiel de Terraform - si vous connaissez d'autres registres comme le registre Docker, vous comprenez déjà l'idée
  • un référentiel Git (personnalisé ou GitHub / BitBucket)
  • une URL HTTP vers une archive .zip avec le module

Mais comment pouvez-vous transmettre les détails des ressources entre les modules?

Dans notre exemple, les serveurs doivent être créés dans un réseau. Alors, comment dire au module «serveur» de créer des VM dans un réseau qui a été créé dans un module appelé «réseau»?

C'est là que l' encapsulation entre en jeu.

Encapsulation du module

L'encapsulation dans Terraform consiste en deux concepts de base: la portée du module et l'exposition explicite aux ressources.

Portée du module

Toutes les instances de ressources, les noms et, par conséquent, la visibilité des ressources, sont isolés dans la portée d'un module. Par exemple, le module «A» ne peut pas voir et ne connaît pas les ressources du module «B» par défaut.

La visibilité des ressources, parfois appelée isolation des ressources, garantit que les ressources auront des noms uniques dans l'espace de noms d'un module. Par exemple, avec nos 5 instances du module "serveur":

module.server[0].resource_type.resource_name module.server[1].resource_type.resource_name module.server[2].resource_type.resource_name ...

D'un autre côté, nous pourrions créer deux instances du même module avec des noms différents:

module "server-alpha" { source = "./module_server" some_variable = some_value } module "server-beta" { source = "./module_server" some_variable = some_value }

Dans ce cas, la dénomination ou l'adresse des ressources serait la suivante:

module.server-alpha.resource_type.resource_name module.server-beta.resource_type.resource_name

Exposition explicite aux ressources

Si vous souhaitez accéder à certains détails des ressources d'un autre module, vous devrez le configurer explicitement.

Par défaut, notre module "serveur" ne connaît pas le réseau qui a été créé dans le module "réseau".

Il faut donc déclarer une outputvaleur dans le module "réseau" pour exporter sa ressource, ou un attribut d'une ressource, vers d'autres modules.

Le module "serveur" doit déclarer a variablepour être utilisé ultérieurement comme entrée:

Cette déclaration explicite de la sortie est le moyen d'exposer une ressource (ou des informations à son sujet) en dehors de la portée du module «racine», donc de la rendre disponible pour d'autres modules.

Ensuite, lorsque nous appelons le module enfant "serveur" dans le module racine, nous devons affecter la sortie du module "réseau" à la variable du module "serveur":

network_id = module.network.network_id

Voici à quoi ressemblera le code final pour appeler nos modules enfants:

module "server" { count = 5 source = "./module_server" some_variable = some_value network_id = module.network.network_id } module "network" { source = "./module_network" some_other_variable = some_other_value }

Cet exemple de configuration créerait 5 instances du même serveur, avec toutes les ressources nécessaires, dans le réseau avec lequel nous avons créé un module séparé.

Emballer

Vous devez maintenant comprendre ce que sont les modules et ce qu'ils font.

Si vous êtes au début de votre voyage Terraform, voici quelques suggestions pour les prochaines étapes.

I encourage you to take this short tutorial from HashiCorp, the creators of Terraform, about modules: "Organize Configuration".

Also, there is a great comprehensive study guide which covers everything from beginner to advanced concepts about Terraform: "Study Guide - Terraform Associate Certification".

The modular code structure makes your configuration more flexible and yet easy to be understood by others. The latter is especially useful for a team.

If you liked the article, follow me on Twitter (@vasylenko) where I occasionally share my findings and tips about Terraform, AWS, Ansible, and other DevOps-related technologies.