Terraform (exemple avec provider AWS)
Terraform est un outil open source d’infrastructure as code, pour construire, changer, versionner l’infrastructure de manière sécurisée, il peut gérer des fournisseurs de services mais aussi des solutions maison sur site.
Aujourd’hui, il dispose de beaucoup de providers comme AWS, Azure et GCP ainsi que beaucoup d’autres (voir la liste sur le site de Hashicorps ici).
L’ensemble d’un projet Terraform s’organise autour de fichiers textes qui peuvent être écrits au format spécifique de Terraform (.tf, le langage est le HCL(HashiCorp Configuration Language)) ou en JSON (.tf.json).
Installation
Cet exemple se déroule sur un terraform v0.11.
wget https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip unzip terraform_0.10.6_linux_amd64.zip mv terraform /usr/local/bin
Création de la conf pour une instance EC2
Terraform lit tous les fichiers .tf ou .tf.json présent dans un répertoire. Nous créons donc un répertoire puis un fichier nommé par exemple « variables.tf » afin d’y déclarer les variables que nous utiliserons :
variable "access_key" {} variable "secret_key" {} variable "key_name" {} variable "aws_region" { default = "eu-west-3" } variable "aws_zone_dispo" { default { "a" = "eu-west-3a" "b" = "eu-west-3b" "c" = "eu-west-3c" } } variable "aws_ami" { default { "eu-west-3" = "ami-0451ae4fd8dd178f7" } } variable "instance_type" { default = "t2.micro" }
Les variables non définies sont à définir dans « terraform.tfvars » :
access_key = "****************" secret_key = "************************************" key_name = "key_test_EC2"
Créer un second fichier appelé par exemple web.tf (car nous souhaitons déployer une VM qui fera par la suite serveur web) :
######################### # Provider # ######################### provider "aws" { access_key = "${var.access_key}" secret_key = "${var.secret_key}" region = "${var.aws_region}" #eu-west-3 } ######################### # Data # ######################### data "aws_vpc" "default" { #VPC par défaut default = true } data "aws_subnet" "default" { #Subnet correspondant à notre VPC par defaut et la zone eu-west-3c filter { name = "vpc-id" values = ["${data.aws_vpc.default.id}"] } filter { name = "availability-zone" values = ["${var.aws_zone_dispo["c"]}"] #eu-west-3c } } ######################### # Security group # ######################### resource "aws_security_group" "web_security" { name = "web_security" description = "Allow 80 et 22" # SSH access from anywhere ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # HTTP access from anywhere ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } ######################### # Create test instance # ######################### resource "aws_instance" "instance_de_test" { ami = "${var.aws_ami[var.aws_region]}" #ou ${lookup(var.aws_ami, var.aws_region)} instance_type = "${var.instance_type}" key_name = "${var.key_name}" security_groups = ["${aws_security_group.web_security.id}"] subnet_id = "${data.aws_subnet.default.id}" } ######################### # Create Elastic IP # ######################### resource "aws_eip" "ElasticIP_instance_de_test" { vpc = true instance = "${aws_instance.instance_de_test.id}" }
Le couple access_key/secret_key correspond pour ma part à un user IAM que j’ai créé spécialement pour terraform attaché à la stratégie « AdministratorAccess » (ce n’est pas une best practise).
Les variables
Nous voyons sur la conf ci-dessus qu’il y a plusieurs types de variables et façons de les appeler.
User variables
string : ${var.foo}
map : ${var.MAP[« KEY »]}
list : ${var.LIST}
Data attributes
${data.TYPE.NAME.ATTRIBUTE}, exemple : ${data.aws_vpc.default.id}
Resources attributes
${TYPE.NAME.ATTRIBUTE}, exemple : ${aws_security_group.web_security.id}
Déployer la conf
Lancer un « terraform init » dans le dossier qui contient nos fichiers de configuration afin d’initialiser ce répertoire de travail.
« terraform plan » permet ensuite de faire un essai à blanc pour s’assurer que tout fonctionnera comme souhaité.
terraform init terraform plan
A chaque modif du code, il est possible de faire un terraform refresh afin qu’il re-balaye la conf
terraform refresh
Pour récupérer la conf actuelle de notre infra (de façon lisible), faire :
terraform show terraform.tfstate
Lancer ensuite le build :
[root@toto ec2]# terraform apply
Une fois le build terminé, un fichier « terraform.tfstate » est créé et contient la conf actuelle de l’infra (dans notre cas de l’instance de notre serveur web)
Pour supprimer faire un :
[root@toto ec2]# terraform apply -destroy (ou terraform destroy)
Les workspaces
Un workspace permet de se mettre dans un contexte utilisant un nouveau tfstate, c’est donc pratique lorsque l’on travaille à plusieurs pour ne pas s’écraser mutuellement l’infra (on part du principe qu’à plusieurs le backend est centralisé sur un bucket S3).
Historique de commandes:
terraform init -backend-config=environments/exp/config.s3.tfbackend -reconfigure
terraform workspace new sam
terraform apply -var-file=environments/exp/terraform.tfvars -var env="sam" (-target=module.blablabla)
terraform apply -var-file=environments/exp/terraform.tfvars -var env="sam" -destroy
terraform workspace select default
terraform workspace delete sam
Ne pas oublier de surcharger une variable présente sur toutes les ressources afin de ne pas avoir de conflit de nom (ici avec -var env= »sam »).
On peut voir la liste des workspace avec un « terraform workspace list », à la création du workspace, terraform créé un nouveau chemin dans le butcket S3 avec le nom de ce dernier (et stocké dans le directory « env: »)