CI/CD
Pipeline Jenkins déclenchée à chaque changement de code : build, tests, analyse, déploiement.
Déploiement d'un site HTML statique via Jenkins, SonarCloud, Docker, Ansible & Terraform sur AWS.
Pipeline Jenkins déclenchée à chaque changement de code : build, tests, analyse, déploiement.
Hébergement sur EC2 avec sécurité réseau (Security Groups) et rôles IAM.
Infrastructure décrite en code avec Terraform : reproductible, traçable, versionnée.
Analyse continue sous SonarCloud (bugs, vulnérabilités, code smells).
Mettre en place une chaîne de bout en bout : analyses qualité/sécurité, build d'image Docker, provisionnement de l'infrastructure, puis déploiement automatisé de l'application sur AWS.
aws configure
)docker
)DOCKERHUB_CREDENTIALS
: utilisateur/token Docker HubAWS_ACCESS_KEY_ID
/ AWS_SECRET_ACCESS_KEY
(ou profil)SONAR_TOKEN
: token SonarCloudhttps://sonarcloud.io
L'infrastructure est décrite en HCL et versionnée sur GitHub. Un terraform apply
crée une instance
EC2, configure les Security Groups (HTTP/HTTPS/SSH) et applique les rôles IAM nécessaires.
devops@DevOps:~/Projet1/terraform$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
+ create
Terraform will perform the following actions:
# aws_eip.web_ip will be created
+ resource "aws_eip" "web_ip" {
+ allocation_id = (known after apply)
+ arn = (known after apply)
+ association_id = (known after apply)
+ carrier_ip = (known after apply)
+ customer_owned_ip = (known after apply)
+ domain = "vpc"
+ id = (known after apply)
+ instance = (known after apply)
+ ipam_pool_id = (known after apply)
+ network_border_group = (known after apply)
+ network_interface = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ ptr_record = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ public_ipv4_pool = (known after apply)
+ region = "eu-west-3"
+ tags = {
+ "Name" = "projet1-staging-web-eip"
}
+ tags_all = {
+ "Name" = "projet1-staging-web-eip"
}
}
# aws_instance.web will be created
+ resource "aws_instance" "web" {
+ ami = "ami-04ec97dc75ac850b1"
+ arn = (known after apply)
+ associate_public_ip_address = false
+ availability_zone = (known after apply)
+ disable_api_stop = (known after apply)
+ disable_api_termination = (known after apply)
+ ebs_optimized = (known after apply)
+ enable_primary_ipv6 = (known after apply)
+ force_destroy = false
+ get_password_data = false
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
+ iam_instance_profile = (known after apply)
+ id = (known after apply)
+ instance_initiated_shutdown_behavior = (known after apply)
+ instance_lifecycle = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = "sshsenan"
+ monitoring = (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ placement_partition_number = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ region = "eu-west-3"
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ spot_instance_request_id = (known after apply)
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "projet1-staging-web-server"
}
+ tags_all = {
+ "Name" = "projet1-staging-web-server"
}
+ tenancy = (known after apply)
+ user_data_base64 = (known after apply)
+ user_data_replace_on_change = false
+ vpc_security_group_ids = (known after apply)
+ capacity_reservation_specification (known after apply)
+ cpu_options (known after apply)
+ ebs_block_device (known after apply)
+ enclave_options (known after apply)
+ ephemeral_block_device (known after apply)
+ instance_market_options (known after apply)
+ maintenance_options (known after apply)
+ metadata_options (known after apply)
+ network_interface (known after apply)
+ primary_network_interface (known after apply)
+ private_dns_name_options (known after apply)
+ root_block_device (known after apply)
}
# aws_internet_gateway.igw will be created
+ resource "aws_internet_gateway" "igw" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ region = "eu-west-3"
+ tags = {
+ "Name" = "staging-igw"
}
+ tags_all = {
+ "Name" = "staging-igw"
}
+ vpc_id = (known after apply)
}
# aws_route_table.public will be created
+ resource "aws_route_table" "public" {
+ arn = (known after apply)
+ id = (known after apply)
+ owner_id = (known after apply)
+ propagating_vgws = (known after apply)
+ region = "eu-west-3"
+ route = [
+ {
+ cidr_block = "0.0.0.0/0"
+ gateway_id = (known after apply)
# (11 unchanged attributes hidden)
},
]
+ tags = {
+ "Name" = "staging-public-rt"
}
+ tags_all = {
+ "Name" = "staging-public-rt"
}
+ vpc_id = (known after apply)
}
# aws_route_table_association.public_a will be created
+ resource "aws_route_table_association" "public_a" {
+ id = (known after apply)
+ region = "eu-west-3"
+ route_table_id = (known after apply)
+ subnet_id = (known after apply)
}
# aws_security_group.projet1_web_sg will be created
+ resource "aws_security_group" "projet1_web_sg" {
+ arn = (known after apply)
+ description = "Allow HTTP and SSH access"
+ egress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ from_port = 0
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 0
# (1 unchanged attribute hidden)
},
]
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ from_port = 22
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 22
# (1 unchanged attribute hidden)
},
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ from_port = 443
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 443
# (1 unchanged attribute hidden)
},
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ from_port = 80
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 80
# (1 unchanged attribute hidden)
},
]
+ name = "projet1_web_sg"
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ region = "eu-west-3"
+ revoke_rules_on_delete = false
+ tags_all = (known after apply)
+ vpc_id = (known after apply)
}
# aws_subnet.public_a will be created
+ resource "aws_subnet" "public_a" {
+ arn = (known after apply)
+ assign_ipv6_address_on_creation = false
+ availability_zone = "eu-west-3a"
+ availability_zone_id = (known after apply)
+ cidr_block = "10.0.1.0/24"
+ enable_dns64 = false
+ enable_resource_name_dns_a_record_on_launch = false
+ enable_resource_name_dns_aaaa_record_on_launch = false
+ id = (known after apply)
+ ipv6_cidr_block_association_id = (known after apply)
+ ipv6_native = false
+ map_public_ip_on_launch = true
+ owner_id = (known after apply)
+ private_dns_hostname_type_on_launch = (known after apply)
+ region = "eu-west-3"
+ tags = {
+ "Name" = "staging-public-a"
}
+ tags_all = {
+ "Name" = "staging-public-a"
}
+ vpc_id = (known after apply)
}
# aws_vpc.main will be created
+ resource "aws_vpc" "main" {
+ arn = (known after apply)
+ cidr_block = "10.0.0.0/16"
+ default_network_acl_id = (known after apply)
+ default_route_table_id = (known after apply)
+ default_security_group_id = (known after apply)
+ dhcp_options_id = (known after apply)
+ enable_dns_hostnames = true
+ enable_dns_support = true
+ enable_network_address_usage_metrics = (known after apply)
+ id = (known after apply)
+ instance_tenancy = "default"
+ ipv6_association_id = (known after apply)
+ ipv6_cidr_block = (known after apply)
+ ipv6_cidr_block_network_border_group = (known after apply)
+ main_route_table_id = (known after apply)
+ owner_id = (known after apply)
+ region = "eu-west-3"
+ tags = {
+ "Name" = "staging-main"
}
+ tags_all = {
+ "Name" = "staging-main"
}
}
terraform plan
puis apply
pour créer l'infrastructure.
L'application statique est packagée dans une image Docker. La pipeline build l'image, la tag
(ex. :latest
et :${BUILD_NUMBER}
) puis la pousse sur Docker Hub.
Dépôt Docker Hub contenant les images du site, prêtes à être déployées.
La pipeline est déclenchée à chaque commit sur la branche principale. Elle orchestre les étapes :
terraform init/plan/apply
À chaque build, un scan SonarCloud détecte bugs, vulnérabilités et code smells. Le tableau de bord permet d'identifier rapidement les points à corriger.
Tableau de bord SonarCloud du projet (qualité & sécurité).
Une fois l'EC2 en place, Ansible installe les dépendances manquantes (Docker, paquets système, etc.) et déploie l'application en conteneur. Les playbooks sont idempotents pour des relances sûres.
Au final, le site HTML statique est accessible depuis l'instance EC2. Chaque modification poussée sur GitHub déclenche une nouvelle livraison automatisée (CI/CD complet).
latest
+ build
) pour des rollbacks immédiats.