PRODUIRE UN INVENTAIRE ANSIBLE AVEC TERRAFORM

Terraform est un bon outil pour créer des machines virtuelles.  Selon sa documentation, il n’est pas fait pour les configurer, ni les gérer.

Ansible est un bon outil pour configurer et gérer les machines virtuelles. Pour ce faire, Ansible aura besoin d’un fichier d’inventaire.

Puisque Terraform a créé les instances, il a toutes les informations pour produire un fichier d’inventaire Ansible. Et Terraform peut afficher de l’information, lui faire écrire un fichier d’inventaire Ansible devrait être simple.

 

Audience cible : Les usagers de Terraform et d’Ansible

Les fichiers mentionnés dans ce document se trouve en copie, à la fin de ce document.

 

Par Guillaume Rompré, Consultant DevOps @ Groupe LINKBYNET

Étape 1, La création des instances

 

Dans le fichier instances.tf, il y a un exemple de configuration Terraform qui crée des instances AWS.

Il décrit un bastion dans un réseau public et six instances dans un réseau privé. Terraform utilisera de simples chaînes de caractères comme variables pour afficher les caractéristiques du bastion créé.

Il décrit aussi une instance privée utilisant la directive « count », ce qui en produira six copies identiques.

Étape 2, Affichage de l’information

 

Dans le fichier output.tf, il y a une ressource de type « local_file » avec un attribut « filename » de valeur « inventaire ». Ce qui va créer un fichier local nommé « inventaire ».  L’attribut « content » reçoit le résultat de la fonction « templatefile » comme valeur.

Cette fonction produit un fichier à partir d’un gabarit et de variables. Ces variables doivent être passées en arguments à la fonction pour qu’elle puisse les voir.

Étape 3, le gabarit

 

Dans le fichier inventory.tmpl, il y a deux groupes Ansible. Un pour le bastion, l’autre pour les autres instances.

Le groupe « bastion » est constitué d’une seule ligne contenant trois variables qui seront remplacées par leur valeur.  Les trois variables sont des chaînes de caractères.

Le groupe « server » est un peu plus complexe. Puisque ses variables sont des « tuples » dans une « list », il nous faut utiliser une boucle « for ». Mais les boucles « for » de Terraform sont conçues pour n’afficher qu’une chaîne de caractères, comme la variable « dns » dans l’exemple.

Comment faire pour qu’une boucle « for » passe à travers une « list » en affichant quelques valeurs depuis un « tuple »? Et, bien sûr, nous voulons passer à travers les « tuples » un à la fois pour que toutes les valeurs sur une ligne de l’inventaire soient liées à la même instance.

Le secret est la variable « index ».

Une boucle « for » dans Terraform exige un point de départ et d’arrivée qui ne peuvent pas être un nombre. Dans cet exemple, la boucle « for » utilise la variable « index » pour passer à travers de la liste des « tuples » et la variable « dns » pour passer à travers les éléments de chaque « tuple ».

 

Regardons de plus près le code dans le fichier de gabarit :

%{ for index, dns in private-dns ~}
${dns} ansible_host=${private-ip[index]} # ${private-id[index]}
%{ endfor ~}

Le %{} indique qu’il s’agit d’une directive et non pas des caractères ou d’une variable.

Les deux ~ retirent tous espaces ou changements de ligne excédentaires. Notez qu’il y en a un au début et un à la fin.

Le ${} indique une substitution.

 

La variable « dns » est une valeur dans « private-dns ».  « private-dns » était connue sous le nom de « aws_instance.i-private.*.private_dns » dans le reste de la configuration Terraform. Pour l’instance « index », la boucle passe, un item à la fois, à travers « index ». « private-ip »  était connue sous le nom de « aws_instance.i-private.*.private_ip » et « private-id » était connue sous le nom de « aws_instance.i-private.*.id ».

Le résultat

 

Voici le fichier d’inventaire Ansible appelé « inventory » :

[bastion]
ec2-99-79-28-17.ca-central-1.compute.amazonaws.com ansible_host=99.79.28.17 # i-0d8c4197a22797

[servers]
ip-10-1-2-114.ca-central-1.compute.internal ansible_host=10.1.2.114 # i-09752396073622ba6
ip-10-1-2-251.ca-central-1.compute.internal ansible_host=10.1.2.251 # i-0af7dddbe098fb1c9
ip-10-1-2-35.ca-central-1.compute.internal ansible_host=10.1.2.35 # i-0de0126836e547eba
ip-10-1-2-109.ca-central-1.compute.internal ansible_host=10.1.2.109 # i-060a362c0165ab3f2
ip-10-1-2-185.ca-central-1.compute.internal ansible_host=10.1.2.185 # i-07bd962925faafa3b
ip-10-1-2-93.ca-central-1.compute.internal ansible_host=10.1.2.93 # i-0fc357d17b62b1497

Conclusion

 

Maintenant la recette connue, il est relativement facile de faire produire un inventaire Ansible par Terraform. C’est la recherche de la recette qui a pris un temps surprenant.

Le fichier instances.tf

### The bastion
resource "aws_instance" "bastion" {
 ami = var.ami
 availability_zone = var.az
 instance_type = "t2.micro"
 key_name = var.ssh-key
 vpc_security_group_ids = [aws_default_security_group.sg-public.id]
 subnet_id = aws_subnet.public.id

 tags = {
  Name = "Bastion"
  Projet = var.projet
  Application = var.Application
 }

 volume_tags = {
  Name = "Bastion"
  Projet = var.projet
  Application = var.Application
 }

 root_block_device {
   volume_size = 8
  }
}

### The Elastic IP for the Bastion
resource "aws_eip" "eip-bastion" {
 vpc = true
 instance = aws_instance.bastion.id
 associate_with_private_ip = aws_instance.bastion.private_ip

 tags = {
  Name = "bastion"
  Projet = var.projet
  Application = var.Application
 }
}

### The private instances
resource "aws_instance" "i-private" {
 ami = var.ami
 availability_zone = var.az
 instance_type = var.ec2_type
 key_name = var.ssh-key
 vpc_security_group_ids = [aws_security_group.sg-private.id]
 subnet_id = aws_subnet.private.id

 tags = {
  Name = "i-private"
  Projet = var.projet
  Application = var.Application
 }

 volume_tags = {
  Name = "i-private"
  Projet = var.projet
  Application = var.Application
 }

 root_block_device {
   volume_size = 8
  }

 ebs_block_device {
  device_name = "/dev/sdb"
  volume_type = var.ebs_type
  volume_size = 8
 }

 count = var.ec2_nb
}

Le fichier outputs.tf

 

### The Ansible inventory file
resource "local_file" "AnsibleInventory" {
 content = templatefile("inventory.tmpl",
 {
  bastion-dns = aws_eip.eip-bastion.public_dns,
  bastion-ip = aws_eip.eip-bastion.public_ip,
  bastion-id = aws_instance.bastion.id,
  private-dns = aws_instance.i-private.*.private_dns,
  private-ip = aws_instance.i-private.*.private_ip,
  private-id = aws_instance.i-private.*.id
 }
 )
 filename = "inventory"
}

Le fichier inventory.tmpl

 

[bastion]
${bastion-dns} ansible_host=${bastion-ip} # ${bastion-id}

[servers]
%{ for index, dns in private-dns ~}
${dns} ansible_host=${private-ip[index]} # ${private-id[index]}
%{ endfor ~}

Vous souhaitez en découvrir davantage ou nous poser vos questions, n’hésitez pas à nous contacter :

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.