Aller au contenu principal

Exercices et Projets


Exercice 1 : Bucket S3 sécurisé

Objectif : Créer un bucket S3 avec toutes les bonnes pratiques de sécurité.

Exigences

  • Chiffrement SSE-S3
  • Versioning activé
  • Bloquer tout accès public
  • Lifecycle : transition vers IA après 30 jours, Glacier après 90 jours
  • Bucket policy pour forcer HTTPS
  • Tags

Solution

AWSTemplateFormatVersion: '2010-09-09'
Description: Exercice 1 - Bucket S3 sécurisé

Parameters:
BucketNamePrefix:
Type: String
Default: secure-data

Environment:
Type: String
AllowedValues: [dev, staging, prod]
Default: dev

Resources:
SecureBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}'

# Chiffrement
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256

# Versioning
VersioningConfiguration:
Status: Enabled

# Bloquer accès public
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true

# Lifecycle
LifecycleConfiguration:
Rules:
- Id: TransitionToIA
Status: Enabled
Transitions:
- StorageClass: STANDARD_IA
TransitionInDays: 30
- StorageClass: GLACIER
TransitionInDays: 90
- Id: DeleteOldVersions
Status: Enabled
NoncurrentVersionExpiration:
NoncurrentDays: 90

# Tags
Tags:
- Key: Environment
Value: !Ref Environment
- Key: Purpose
Value: SecureDataStorage

BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref SecureBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: EnforceHTTPS
Effect: Deny
Principal: '*'
Action: 's3:*'
Resource:
- !GetAtt SecureBucket.Arn
- !Sub '${SecureBucket.Arn}/*'
Condition:
Bool:
'aws:SecureTransport': 'false'

Outputs:
BucketName:
Description: Nom du bucket
Value: !Ref SecureBucket

BucketArn:
Description: ARN du bucket
Value: !GetAtt SecureBucket.Arn

Exercice 2 : VPC complet

Objectif : Créer un VPC avec subnets publics/privés, NAT Gateway et routes.

Architecture

Template

AWSTemplateFormatVersion: '2010-09-09'
Description: Exercice 2 - VPC complet

Parameters:
VPCCidr:
Type: String
Default: '10.0.0.0/16'

Environment:
Type: String
Default: dev

Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-vpc'

# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-igw'

VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway

# Public Subnets
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [0, !Cidr [!Ref VPCCidr, 16, 8]]
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-1'

PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [1, !Cidr [!Ref VPCCidr, 16, 8]]
AvailabilityZone: !Select [1, !GetAZs '']
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-2'

# Private Subnets
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [10, !Cidr [!Ref VPCCidr, 16, 8]]
AvailabilityZone: !Select [0, !GetAZs '']
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-1'

PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [11, !Cidr [!Ref VPCCidr, 16, 8]]
AvailabilityZone: !Select [1, !GetAZs '']
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-2'

# NAT Gateway
NATGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: VPCGatewayAttachment
Properties:
Domain: vpc

NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NATGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-nat'

# Route Tables
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-rt'

PublicRoute:
Type: AWS::EC2::Route
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway

PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable

PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable

PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-private-rt'

PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NATGateway

PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable

PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable

Outputs:
VPCId:
Description: VPC ID
Value: !Ref VPC
Export:
Name: !Sub '${AWS::StackName}-VPCId'

PublicSubnets:
Description: Public subnet IDs
Value: !Join [',', [!Ref PublicSubnet1, !Ref PublicSubnet2]]
Export:
Name: !Sub '${AWS::StackName}-PublicSubnets'

PrivateSubnets:
Description: Private subnet IDs
Value: !Join [',', [!Ref PrivateSubnet1, !Ref PrivateSubnet2]]
Export:
Name: !Sub '${AWS::StackName}-PrivateSubnets'

Exercice 3 : Application Web avec ALB

Objectif : Déployer une application web avec Auto Scaling et Load Balancer.

Exigences

  • ALB avec HTTPS (certificat ACM)
  • Auto Scaling Group (2-10 instances)
  • Launch Template avec UserData
  • Security Groups appropriés
  • Health checks

Template (extrait)

AWSTemplateFormatVersion: '2010-09-09'
Description: Exercice 3 - Application Web avec ALB

Parameters:
VPCStackName:
Type: String
Description: Nom de la stack VPC (exercice 2)

InstanceType:
Type: String
Default: t3.micro

MinSize:
Type: Number
Default: 2

MaxSize:
Type: Number
Default: 10

Resources:
# Security Groups
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ALB Security Group
VpcId: !ImportValue
!Sub '${VPCStackName}-VPCId'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0

AppSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Application Security Group
VpcId: !ImportValue
!Sub '${VPCStackName}-VPCId'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId: !Ref ALBSecurityGroup

# Application Load Balancer
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub '${AWS::StackName}-alb'
Type: application
Scheme: internet-facing
SecurityGroups:
- !Ref ALBSecurityGroup
Subnets: !Split
- ','
- !ImportValue
!Sub '${VPCStackName}-PublicSubnets'

TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub '${AWS::StackName}-tg'
Port: 80
Protocol: HTTP
VpcId: !ImportValue
!Sub '${VPCStackName}-VPCId'
HealthCheckPath: /health
HealthCheckIntervalSeconds: 30
HealthyThresholdCount: 2
UnhealthyThresholdCount: 3
TargetType: instance

HTTPListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup

# Launch Template
LaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: !Sub '${AWS::StackName}-lt'
LaunchTemplateData:
ImageId: !Sub '{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}'
InstanceType: !Ref InstanceType
SecurityGroupIds:
- !Ref AppSecurityGroup
UserData:
Fn::Base64: !Sub |
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd

INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
cat > /var/www/html/index.html <<EOF
<!DOCTYPE html>
<html>
<head><title>CloudFormation Demo</title></head>
<body>
<h1>Hello from CloudFormation!</h1>
<p>Instance: $INSTANCE_ID</p>
<p>Stack: ${AWS::StackName}</p>
<p>Region: ${AWS::Region}</p>
</body>
</html>
EOF

echo "OK" > /var/www/html/health

# Auto Scaling Group
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub '${AWS::StackName}-asg'
LaunchTemplate:
LaunchTemplateId: !Ref LaunchTemplate
Version: !GetAtt LaunchTemplate.LatestVersionNumber
MinSize: !Ref MinSize
MaxSize: !Ref MaxSize
DesiredCapacity: !Ref MinSize
VPCZoneIdentifier: !Split
- ','
- !ImportValue
!Sub '${VPCStackName}-PrivateSubnets'
TargetGroupARNs:
- !Ref TargetGroup
HealthCheckType: ELB
HealthCheckGracePeriod: 300
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-instance'
PropagateAtLaunch: true

# Scaling Policies
ScaleUpPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AutoScalingGroupName: !Ref AutoScalingGroup
PolicyType: TargetTrackingScaling
TargetTrackingConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ASGAverageCPUUtilization
TargetValue: 70

Outputs:
LoadBalancerDNS:
Description: URL de l'application
Value: !Sub 'http://${ApplicationLoadBalancer.DNSName}'

Projet final : Infrastructure complète

Objectif : Créer une infrastructure de production complète avec nested stacks.

Architecture

Structure

project/
├── main.yaml
├── templates/
│ ├── network.yaml
│ ├── security.yaml
│ ├── database.yaml
│ └── application.yaml
├── parameters/
│ ├── dev.json
│ └── prod.json
└── deploy.sh

Déploiement

#!/bin/bash
# deploy.sh

STACK_NAME="production-infra"
ENVIRONMENT="prod"
REGION="eu-west-1"
BUCKET="my-cfn-templates-bucket"

# Upload templates
aws s3 sync ./templates s3://${BUCKET}/templates/

# Deploy
aws cloudformation deploy \
--template-file main.yaml \
--stack-name ${STACK_NAME} \
--parameter-overrides \
Environment=${ENVIRONMENT} \
TemplateBucket=${BUCKET} \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--region ${REGION}

# Get outputs
aws cloudformation describe-stacks \
--stack-name ${STACK_NAME} \
--query 'Stacks[0].Outputs' \
--region ${REGION}

Récapitulatif du cours

Félicitations ! Vous avez complété le cours AWS CloudFormation.

Ce que vous avez appris

  1. ✅ Concepts et workflow CloudFormation
  2. ✅ Structure des templates YAML/JSON
  3. ✅ Ressources AWS
  4. ✅ Paramètres et validation
  5. ✅ Mappings et conditions
  6. ✅ Outputs et exports cross-stack
  7. ✅ Fonctions intrinsèques
  8. ✅ Nested stacks et StackSets
  9. ✅ Bonnes pratiques et sécurité
  10. ✅ Projets pratiques

Prochaines étapes

  • Pratiquez avec les exercices
  • Explorez AWS SAM pour les applications serverless
  • Découvrez CDK pour l'IaC en code

← Bonnes Pratiques | Table des matières →