Chargement en cours

Packer, Terraform, et AWS

Packer, Terraform, et AWS

Packer, Terraform, Bucket S3, et DynamoDB…

Que de péripéties entre vie pro et vie perso ces derniers mois… mais on reprend en douceur les articles !

Après plusieurs articles sur Packer et Terraform, que ce soit pour déployer du Windows sur vSphere, du GNU/Linux sur Proxmox, je me suis dit qu’il était enfin temps de commencer à bidouiller un peu niveau AWS. D’autant plus que j’utilise pas mal ce CSP au boulot.

I) Le labo

L’idée ici est assez simple, on va utiliser Packer pour créer une AMI, Amazon Machine Image, donc un template pour instance EC2, puis utiliser Terraform pour cloner ce même template. Du classique en somme ! Sauf qu’ici on en profitera pour stocker notre fichier tfstate sur un bucket S3, et utiliser DynamoDB comme lock, chose dont je n’avais pas parlé dans mes précédents articles~

Comme j’ai dit, on reprend en douceur.

II) Installation de Packer et création de la policy IAM

Comme pour mes précédents articles, on repart sur un petit Devcontainer avec un Dockerfile histoire de faciliter la chose.

Bien, pour Packer on créer un simple fichier ubuntu.pkr.hcl, et on y décrit notre plugin AWS puis notre image AMI:

// Ubuntu Amazon Machine Image

packer {
  required_plugins {
    amazon = {
      source  = "github.com/hashicorp/amazon"
      version = "~> 1"
    }
  }
}

variable "aws_region" {
  type    = string
  default = "eu-west-3" // Paris
}

source "amazon-ebs" "ubuntu" {
  region      = var.aws_region
  instance_type = "t2.micro"
  ami_name    = "ubuntu-20.04-minimal-{{timestamp}}"
  source_ami_filter {
    filters = {
      name                = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
      root-device-type    = "ebs"
      virtualization-type = "hvm"
    }
    owners      = ["099720109477"] // Ubuntu official
    most_recent = true
  }
  ssh_username = "ubuntu"
}

build {
  sources = ["source.amazon-ebs.ubuntu"]

  provisioner "shell" {
    inline = [
      "sudo apt update",
      "sudo apt upgrade -y"
    ]
  }
}

Reste plus qu’à faire un petit packer init ubuntu.pkr.hcl~

Une fois notre provider installé, on va gérer la partie Users/Permissions avant de s’attaquer à la technique pur et dur.

III) Création de la policy IAM, et génération de la clé d’accès pour AWS-Cli

On se rend donc dans l’onglet IAM sur AWS, puis Create policy. Ici j’ai purement et simplement demandé à un LLM de me fournir les droits adéquats pour ce genre de lab, à vous d’adapter au besoin ! Mais dans les faits, il nous suffit de coller notre JSON et on peut valider:

On renseigne un nom, une description, et on a un joli aperçu des droits pour chaque services:

Et le tour est joué ! Ensuite, il suffit d’attacher cette policy au user que l’on voudra créer/utiliser:

Et enfin la dernière étape, générer la clé pour pouvoir se connecter à notre Infra. Rien de très sorcier, toujours dans IAM, on se rend sur Users, puis Create access key:

Ici on choisi le premier Use case, à savoir Command Line Interface (CLI), et on peut poursuivre.

On sauvegarde ensuite précieusement l’ID de la key et le secret associé, et on peut les encoder dans notre .aws/credentials :

Et en profiter pour spécifier notre région préférée dans .aws/config, histoire de ne pas devoir le ré-encoder partout dans notre code 🙂

Au passage, par défaut Packer/Terraform utiliseront le profil [default], mais si avez plusieurs profils, il suffit d’exporter la variable AWS_PROFILE et spécifier celui que vous souhaitez utiliser:

Bien, on peut enfin commencer à tâter du code !

IV) Création de l’AMI via Packer

Déjà décrit plus haut, et déjà initialisé, reste donc plus qu’à faire un packer build ubuntu.pkr.hcl et on est bon 😁

Après quelques minutes:

Déjà une bonne chose de faite ! Maintenant, on va passer à la création de notre bucket S3 et de notre table DynamoDB pour le lock du fichier tfstate.

V) Comment utiliser Terraform à plusieurs~

Bon je vais pas la faire longue ici car c’est un des fondamentaux quand on utilise Terraform.

Imaginez que vous soyez à 3-4 à travailler sur un projet, qu’est-ce qu’il se passerait si au même moment, deux collègues lancent un terraform apply ? Et bien un sacré foutoir ma bonne dame, voilà voilà.

Du coup, l’idée est de stocker notre fichier tfstatequi contient donc pour rappel l’état de notre infrastructure– sur un share commun, et de faire en sorte que lorsque quelqu’un lance un terraform apply, cela trigger un lock dans DynamoDB, histoire de dire aux autres « tu ne peux pas lancer maintenant, quelqu’un est déjà occupé« .

Du coup, on va créer une arborescence comme ceci :

  • terraform
    • /setup-backend/
      • s3-dynamodb.tf, un simple fichier Terraform qu’on pourra ré-utiliser pour d’autres projets, et qui va juste nous permettre de créer notre bucket S3 et notre table Dynamodb. On aurait pu le faire via AWS-CLI directement ;
    • /clone-ec2/
      • backend.tf, notre fichier qui va indiquer à Terraform d’utiliser un backend distant plutôt que local ;
      • main.tf, que l’on verra plus bas, et qui va simplement cloner en X fois notre instance AMI créée en début via Packer ;

Voilà donc le script:

provider "aws" {
  region = "eu-west-3"
}

# Bucket S3
resource "aws_s3_bucket" "tf_state_bucket" {
  bucket = "tips4tech-tfstate-labo-euw3"
}

# Table DynamoDB
resource "aws_dynamodb_table" "tf_lock_table" {
  name         = "tips4tech-tf-lock-labo-euw3"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }
}

Rien de très complexe ici encore, on peut donc terraform init, puis réaliser notre terraform apply.

Et c’est tout bon ! 😎

Maintenant, on peut se rendre dans notre folder clone-ec2 pour déclarer notre backend dans backend.tf, puis réaliser un terraform init:

terraform {
  backend "s3" {
    bucket         = "tips4tech-tfstate-labo-euw3"
    key            = "terraform.tfstate"
    region         = "eu-west-3"
    dynamodb_table = "tips4tech-tf-lock-labo-euw3"
    encrypt        = true
  }
}

It works !
Bon ici j’aurai pû (j’aurais dû) mieux agencer mon arborescence, car bon on aura installé 2x le provider AWS (un dans chaque folder…), rien de dramatique mais en général en entreprise on fait ça bien plus proprement.

Voici le script concernant le main.tf:

provider "aws" {
  region = "eu-west-3"
}

variable "instance_count" {
  default = 3
}

resource "aws_instance" "ubuntu" {
  ami           = "ami-04b30ef4fa16d79b4" # Adapt the AMI number created by Packer
  instance_type = "t2.micro"

  count = var.instance_count

  tags = {
    Name = "packer-ubuntu-${count.index}"
  }
}

Comme mis en commentaire, bien adapter l’ID de l’AMI créée par Packer:

On peut donc enfin faire un terraform apply histoire de cloner en 3 exemplaires notre template AMI:

On peut déjà voir ici la ligne Acquiring state lock, c’est bon signe ! On tape un yes et on laisse la magie opérer~

VI) Conclusion

Je pensais être un peu rouillé car je ne fais du Terraform qu’assez rarement, mais au final même si y’a clairement moyen de faire plus propre, on a quelque chose de fonctionnel ! 😁

J’espère donc vous avoir appris quelques bricoles, et je ferai sûrement d’autres articles autour d’AWS !

Sur ce, belle journée/soirée à vous, ciao !

2 comments

comments user
Ludo

attention, le lock dynamo db va bientôt être déprécié, on peut utiliser le lock dans un fichier d’un bucket s3 à la place.

    comments user
    Anthony

    Effectivement, my bad ! Je viens de voir que ça date de quelques mois seulement, merci à toi, j’adapterai pour les prochains articles 😉