Produire un inventaire Ansible avec Terraform

  • 5m

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 ~}

[PAROLES D'EXPERTS] Mise en conformité autour de la Loi 25 : une étape indispensable de la transformation des entreprises Québécoises

La protection des renseignements personnels fait depuis quelques années l’objet de nombreuses initiatives de citoyens et d’organismes étatiques dans le monde. Car les entreprises commerciales, GAFAM en tête, mais aussi de nombreux acteurs installés localement, ont eu conscience très tôt de la valeur des données personnelles qu’elles récoltaient. Elles ont ainsi longtemps pu profiter, sans contrepartie, d’un vide juridique en matière de récolte et d’usage de ces dernières qui leur a offert un avantage concurrentiel capital sur leurs marchés respectifs.

[PAROLES D'EXPERTS] Audit d'une plateforme AWS, comment attribuer les bons droits ?

La sécurité est un vieux sujet en TI mais elle prend de plus en plus d’ampleur avec les années et les cas que l’on peut entendre dans la presse spécialisée ou non ces dernières années.

Pour différentes raisons, on peut être amené à se faire auditer par un organisme externe.

[PAROLES D'EXPERTS] Renforcer la sécurité AWS grâce au standard de sécurité PCI DSS

La norme de sécurité de l’industrie des cartes de paiement (Payment Card Industry Data Security Standard), mieux connue sous la dénomination PCI DSS, est un standard de sécurité mis en place par les grands acteurs de la chaîne monétique. Son but ? Augmenter la sécurité et le contrôle des informations associées au détenteur d’une carte de crédit lorsque celui-ci effectue un achat et réduire par la même occasion les possibilités d’utilisation frauduleuse de son moyen de paiement.