Aller au contenu
Infrastructure as Code medium
🔐 Alerte sécurité — Incident supply chain Trivy : lire mon analyse de l'attaque

Terraform AWS — Rôle IAM et instance profile

14 min de lecture

logo terraform

Une EC2 ne devrait jamais “tout pouvoir faire” par défaut. Dès qu’une machine doit lire un bucket S3, interroger une API AWS ou décrire d’autres ressources, vous devez choisir exactement quelles permissions lui donner. Sinon, la moindre fuite de credentials ou la moindre erreur de configuration ouvre beaucoup trop de droits. Ce guide introduit donc le trio essentiel d’AWS côté identité : policy, rôle et instance profile.

L’enchaînement peut sembler abstrait au début, donc retenez cette image simple : la policy est la liste des droits, le rôle est le conteneur qui porte ces droits, et l’instance profile est l’adaptateur qui permet d’attacher ce rôle à une EC2. Le but du guide est de rendre cette chaîne concrète, pas seulement de vous faire copier trois ressources HCL.

  • Créer une police IAM avec aws_iam_policy_document (JSON as code)
  • Créer un rôle IAM avec une trust policy pour EC2
  • Attacher une police à un rôle avec aws_iam_role_policy_attachment
  • Créer un instance profile pour lier le rôle à une EC2
  • Ajouter le profil à une instance via iam_instance_profile
  • Vérifier les ARNs dans les outputs

Après le provider et le réseau, la question suivante est logique : que peut faire l’instance une fois démarrée ? Dans AWS, la réponse passe par IAM. Contrairement à un script local ou à un environnement de test bricolé, une EC2 ne devrait pas embarquer des clés statiques dans ses fichiers.

Ce guide montre donc la bonne direction dès le début : donner à l’instance un rôle ciblé, avec des permissions explicites et réutilisables.

Construire une infrastructure avec :

  1. Une policy IAM qui permet de lire depuis S3 et décrire des instances
  2. Un rôle IAM qui assume cette policy
  3. Un instance profile qui lie le rôle
  4. Une EC2 qui hérite des permissions via le profil

Durée estimée : 15 minutes Coût : Gratuit (tier gratuit AWS)

Créez le répertoire du lab :

Fenêtre de terminal
mkdir -p ~/terraform-aws-iam-ec2
cd ~/terraform-aws-iam-ec2

Créez versions.tf :

terraform {
required_version = ">= 1.11.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}

Créez variables.tf :

variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
variable "instance_name" {
description = "Name tag for the EC2 instance"
type = string
default = "lab03-instance-with-iam"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "role_name" {
description = "Name of the IAM role"
type = string
default = "lab03-ec2-role"
}

Créez main.tf avec la data source aws_iam_policy_document :

# Créer une policy IAM document (JSON policy as code)
data "aws_iam_policy_document" "s3_read_policy" {
statement {
effect = "Allow"
actions = [
"s3:GetObject",
"s3:ListBucket"
]
resources = [
"arn:aws:s3:::*",
"arn:aws:s3:::*/*"
]
}
statement {
effect = "Allow"
actions = ["ec2:DescribeInstances"]
resources = ["*"]
}
}

Ce bloc définit :

  • Statement 1 : Lire depuis S3 (ListBucket, GetObject) sur tous les buckets
  • Statement 2 : Décrire les instances EC2 (lire les métadonnées)

La data source génère automatiquement le JSON — pas besoin de l’écrire manuellement.

Toujours dans main.tf, ajoutez :

resource "aws_iam_role" "lab03_role" {
name = var.role_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
tags = {
Name = var.role_name
}
}

Ce bloc crée :

  • Un rôle IAM = conteneur de permissions
  • Une trust policy = qui peut utiliser ce rôle ?
    • Principal: { Service: "ec2.amazonaws.com" } = Les instances EC2 peuvent assumer ce rôle
    • Action: "sts:AssumeRole" = Permission pour les EC2 de prendre le rôle

Toujours dans main.tf, ajoutez :

resource "aws_iam_policy" "s3_read_policy" {
name = "lab03-s3-read-policy"
description = "Allow EC2 to read from S3 and describe instances"
policy = data.aws_iam_policy_document.s3_read_policy.json
tags = {
Name = "lab03-s3-read-policy"
}
}

Ce bloc :

  • Crée une policy managée AWS à partir du document défini plus haut
  • policy = data.aws_iam_policy_document.s3_read_policy.json : utilise le JSON généré

Toujours dans main.tf, ajoutez :

resource "aws_iam_role_policy_attachment" "attach_s3_policy" {
role = aws_iam_role.lab03_role.name
policy_arn = aws_iam_policy.s3_read_policy.arn
}

Ce bloc dit :

  • “Attache la policy s3_read_policy au rôle lab03_role
  • L’EC2 pourra maintenant faire ce que la policy autorise

Toujours dans main.tf, ajoutez :

resource "aws_iam_instance_profile" "lab03_profile" {
name = "lab03-ec2-profile"
role = aws_iam_role.lab03_role.name
}

L’instance profile :

  • Est le lien entre rôle IAM et EC2
  • Contient un ou plusieurs rôles (généralement un seul)
  • Est attaché à une instance, pas directement au rôle

Toujours dans main.tf, ajoutez :

# Lire l'AMI Ubuntu la plus récente
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical (Ubuntu)
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "lab03_vm" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
iam_instance_profile = aws_iam_instance_profile.lab03_profile.name
tags = {
Name = var.instance_name
}
}

Clé :

  • iam_instance_profile = aws_iam_instance_profile.lab03_profile.name : l’EC2 hérite des permissions du profil

Créez outputs.tf :

output "iam_role_arn" {
description = "ARN of the IAM role"
value = aws_iam_role.lab03_role.arn
}
output "iam_role_name" {
description = "Name of the IAM role"
value = aws_iam_role.lab03_role.name
}
output "iam_policy_arn" {
description = "ARN of the IAM policy"
value = aws_iam_policy.s3_read_policy.arn
}
output "instance_profile_arn" {
description = "ARN of the instance profile"
value = aws_iam_instance_profile.lab03_profile.arn
}
output "instance_id" {
description = "EC2 Instance ID"
value = aws_instance.lab03_vm.id
}
output "instance_arn" {
description = "EC2 Instance ARN"
value = aws_instance.lab03_vm.arn
}
output "instance_iam_instance_profile" {
description = "IAM instance profile name attached to the instance"
value = aws_instance.lab03_vm.iam_instance_profile
}

Créez terraform.tfvars :

aws_region = "us-east-1"
instance_name = "lab03-instance-with-iam"
instance_type = "t2.micro"
role_name = "lab03-ec2-role"
  1. Initialiser :

    Fenêtre de terminal
    terraform init
  2. Valider :

    Fenêtre de terminal
    terraform validate
  3. Appliquer :

    Fenêtre de terminal
    terraform apply -auto-approve

    Sortie attendue (fin) :

    Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
    Outputs:
    iam_policy_arn = "arn:aws:iam::276757567417:policy/lab03-s3-read-policy"
    iam_role_arn = "arn:aws:iam::276757567417:role/lab03-ec2-role"
    iam_role_name = "lab03-ec2-role"
    instance_arn = "arn:aws:ec2:us-east-1:276757567417:instance/i-046ec132d2c5984af"
    instance_iam_instance_profile = "lab03-ec2-profile"
    instance_id = "i-046ec132d2c5984af"
    instance_profile_arn = "arn:aws:iam::276757567417:instance-profile/lab03-ec2-profile"
  4. Vérifier dans la console AWS (optionnel) :

    Fenêtre de terminal
    # Voir le rôle créé
    aws iam get-role --role-name lab03-ec2-role
    # Voir l'instance profile
    aws iam get-instance-profile --instance-profile-name lab03-ec2-profile
TypeRôleExemple
Trust policy”Qui peut utiliser ce rôle ?”EC2 service, Lambda, utilisateurs
Resource policy”Quelles actions sont autorisées ?”S3 read, EC2 describe

Dans ce lab :

  • Trust policy du rôle : “les EC2 peuvent l’assumer”
  • Resource policy (la policy S3) : “lire depuis S3 et décrire instances”

Cet data source génère du JSON IAM à partir de HCL lisible. Avant :

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::*", "arn:aws:s3:::*/*"]
}
]
}

Avec aws_iam_policy_document, c’est généré automatiquement et testable.

# ❌ Trop permissif
actions = ["*"]
# ✅ Spécifique
actions = [
"s3:GetObject",
"s3:ListBucket"
]
# ❌ Tout
resources = ["*"]
# ✅ Spécifique
resources = [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
# ❌ Un rôle pour tout
role_name = "general-role"
# ✅ Rôles spécialisés
resource "aws_iam_role" "ec2_s3_role" { ... }
resource "aws_iam_role" "lambda_dynamo_role" { ... }
data "aws_iam_policy_document" "example" {
description = "Allow EC2 to read production S3 bucket only"
# ...
}
SymptômeCauseSolution
Error: User is not authorized to perform: iam:CreateRoleUtilisateur AWS sans permissions IAMDemander IAMFullAccess ou permissions iam:CreateRole, iam:CreatePolicy, etc.
Error: EntityAlreadyExists: Role with name lab03-ec2-role already existsRôle existe déjàChanger role_name ou terraform destroy d’abord
EC2 créée mais profil absentTiming ou oubli iam_instance_profileAjouter iam_instance_profile = aws_iam_instance_profile.lab03_profile.name
Policy attachée mais EC2 ne peut pas lire S3Trust policy manquanteVérifier que assume_role_policy permet EC2
Fenêtre de terminal
terraform destroy -auto-approve

Sortie attendue :

Destroy complete! Resources: 5 destroyed.

Nettoyez les fichiers :

Fenêtre de terminal
rm -rf .terraform* terraform.tfstate*
  1. IAM = sécurité AWS — toujours utiliser le moindre privilège
  2. Policy document genère du JSON depuis du HCL lisible
  3. Rôle IAM = conteneur de permissions avec trust policy
  4. Instance profile = lien entre rôle et EC2
  5. Trust policy répond “Qui peut utiliser ce rôle ?”
  6. Resource policy répond “Qu’est-ce qui est autorisé ?”
  7. Attacher plutôt que d’intégrer : utilisez aws_iam_role_policy_attachment pour la réutilisabilité

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn