Accueil/Compétences/AWS CloudFormation
Logo AWS CloudFormation

AWS CloudFormation

Le service Infrastructure as Code d'AWS qui permet de modéliser et provisionner vos ressources cloud de manière automatisée, cohérente et reproductible.

Pour les non-initiés

Qu'est-ce qu'AWS CloudFormation ?

Imaginez que vous construisez une ville complexe. Au lieu de construire chaque bâtiment un par un manuellement, vous pourriez dessiner un plan détaillé de la ville et laisser des machines construire tout automatiquement selon vos spécifications.

AWS CloudFormation fonctionne de manière similaire pour le cloud. C'est un service qui permet de définir toute votre infrastructure AWS (serveurs, bases de données, réseaux, etc.) sous forme de "plans" textuels (appelés templates) qui décrivent exactement ce que vous voulez construire.

Pourquoi est-ce important ?

Reproductibilité

Vous pouvez recréer exactement le même environnement dans différentes régions ou comptes AWS, garantissant cohérence et fiabilité.

Automatisation et Sécurité

Élimine les erreurs humaines lors de la configuration manuelle des ressources, tout en permettant de contrôler et vérifier les changements avant leur application.

En résumé, CloudFormation transforme la manière dont les entreprises gèrent leur infrastructure cloud, en passant de processus manuels sujets aux erreurs à une approche programmable, versionnable et automatisée.

Pour les développeurs

Fonctionnement technique

AWS CloudFormation est un service qui utilise des templates pour décrire, provisionner et gérer des ressources AWS et tierces de façon ordonnée et prévisible. Ces templates peuvent être écrits en JSON ou YAML et sont traités comme du code, permettant la gestion de version, la revue et la réutilisation.

Les concepts fondamentaux

Structure d'un template

Un template CloudFormation comprend plusieurs sections principales:

  • Format Version : Version du template (ex: "2010-09-09")
  • Description : Description textuelle du template
  • Parameters : Valeurs que l'utilisateur peut personnaliser lors du déploiement
  • Mappings : Tables de mappage pour configurer des valeurs conditionnelles
  • Conditions : Conditions pour la création conditionnelle de ressources
  • Resources : Définition des ressources AWS à créer (section obligatoire)
  • Outputs : Valeurs renvoyées après la création de la stack

Template de base

Exemple de template CloudFormation créant un Icône bucket S3bucket S3 et une Icône instance EC2instance EC2 :

template-de-base.yaml
AWSTemplateFormatVersion: '2010-09-09' Description: 'Template basique pour créer un bucket S3 et une instance EC2' Parameters: InstanceType: Description: Type d'instance EC2 Type: String Default: t2.micro AllowedValues: - t2.micro - t2.small - t3.micro ConstraintDescription: Doit être un type d'instance valide BucketName: Description: 'Nom du bucket S3 (unique globalement)' Type: String Default: my-unique-bucket-name Environment: Description: 'Environnement de déploiement' Type: String Default: dev AllowedValues: - dev - staging - prod Resources: # Instance EC2 WebServer: Type: AWS::EC2::Instance Properties: InstanceType: !Ref InstanceType ImageId: ami-01234567890abcdef # AMI Amazon Linux 2 (remplacer par l'AMI appropriée) SecurityGroups: - !Ref WebServerSecurityGroup Tags: - Key: Name Value: !Sub 'WebServer-${Environment}' - Key: Environment Value: !Ref Environment UserData: Fn::Base64: | #!/bin/bash -xe yum update -y yum install -y httpd systemctl start httpd systemctl enable httpd echo "<html><body><h1>Hello from CloudFormation!</h1></body></html>" > /var/www/html/index.html # Groupe de sécurité pour l'instance EC2 WebServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP and SSH access SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 0.0.0.0/0 Tags: - Key: Environment Value: !Ref Environment # Bucket S3 S3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName AccessControl: Private VersioningConfiguration: Status: Enabled Tags: - Key: Environment Value: !Ref Environment Outputs: WebsiteURL: Description: URL du serveur web Value: !Sub 'http://${WebServer.PublicDnsName}' BucketName: Description: Nom du bucket S3 créé Value: !Ref S3Bucket BucketARN: Description: ARN du bucket S3 Value: !GetAtt S3Bucket.Arn

Architecture Serverless

Exemple de template pour une API Serverless avec Icône API GatewayAPI Gateway, Icône LambdaLambda et Icône DynamoDBDynamoDB :

serverless-api.yaml
AWSTemplateFormatVersion: '2010-09-09' Description: 'Template Serverless API avec API Gateway, Lambda et DynamoDB' Parameters: StageName: Type: String Default: dev Description: Stage name for API Gateway deployment TableReadCapacity: Type: Number Default: 5 Description: Read capacity units for DynamoDB table TableWriteCapacity: Type: Number Default: 5 Description: Write capacity units for DynamoDB table Resources: # Fonction Lambda pour traiter les requêtes API ApiFunction: Type: AWS::Lambda::Function Properties: Handler: index.handler Role: !GetAtt ApiFunctionRole.Arn Code: ZipFile: | exports.handler = async (event) => { const dynamoDb = new (require('aws-sdk')).DynamoDB.DocumentClient(); try { if (event.httpMethod === 'GET') { const params = { TableName: process.env.TABLE_NAME, }; const result = await dynamoDb.scan(params).promise(); return { statusCode: 200, body: JSON.stringify(result.Items) }; } else if (event.httpMethod === 'POST') { const data = JSON.parse(event.body); const params = { TableName: process.env.TABLE_NAME, Item: { id: Date.now().toString(), content: data.content, createdAt: new Date().toISOString() } }; await dynamoDb.put(params).promise(); return { statusCode: 201, body: JSON.stringify(params.Item) }; } return { statusCode: 400, body: JSON.stringify({ message: 'Unsupported method' }) }; } catch (error) { console.error(error); return { statusCode: 500, body: JSON.stringify({ message: 'Internal server error' }) }; } } Runtime: nodejs14.x Timeout: 10 MemorySize: 128 Environment: Variables: TABLE_NAME: !Ref ItemsTable # Rôle IAM pour la fonction Lambda ApiFunctionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: DynamoDBAccess PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem - dynamodb:UpdateItem - dynamodb:DeleteItem - dynamodb:Scan - dynamodb:Query Resource: !GetAtt ItemsTable.Arn # Table DynamoDB ItemsTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: !Ref TableReadCapacity WriteCapacityUnits: !Ref TableWriteCapacity # API Gateway REST API ApiGateway: Type: AWS::ApiGateway::RestApi Properties: Name: ServerlessAPI Description: API pour accéder aux données DynamoDB via Lambda # Ressource API Gateway ApiResource: Type: AWS::ApiGateway::Resource Properties: RestApiId: !Ref ApiGateway ParentId: !GetAtt ApiGateway.RootResourceId PathPart: items # Méthode GET ApiGatewayGetMethod: Type: AWS::ApiGateway::Method Properties: RestApiId: !Ref ApiGateway ResourceId: !Ref ApiResource HttpMethod: GET AuthorizationType: NONE Integration: Type: AWS_PROXY IntegrationHttpMethod: POST Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations # Méthode POST ApiGatewayPostMethod: Type: AWS::ApiGateway::Method Properties: RestApiId: !Ref ApiGateway ResourceId: !Ref ApiResource HttpMethod: POST AuthorizationType: NONE Integration: Type: AWS_PROXY IntegrationHttpMethod: POST Uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiFunction.Arn}/invocations # Déploiement API Gateway ApiDeployment: Type: AWS::ApiGateway::Deployment DependsOn: - ApiGatewayGetMethod - ApiGatewayPostMethod Properties: RestApiId: !Ref ApiGateway # Stage API Gateway ApiStage: Type: AWS::ApiGateway::Stage Properties: RestApiId: !Ref ApiGateway DeploymentId: !Ref ApiDeployment StageName: !Ref StageName # Permission pour API Gateway d'invoquer Lambda ApiGatewayPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !Ref ApiFunction Principal: apigateway.amazonaws.com SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGateway}/*/* Outputs: ApiEndpoint: Description: URL de l'API Value: !Sub https://${ApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/items TableName: Description: Nom de la table DynamoDB Value: !Ref ItemsTable

Stacks imbriqués

Les stacks imbriqués permettent de diviser l'infrastructure en modules réutilisables et de gérer des architectures complexes :

nested-stacks.yaml
# Template principal (main-stack.yaml) AWSTemplateFormatVersion: '2010-09-09' Description: 'Template principal avec stacks imbriqués' Parameters: Environment: Type: String Default: dev AllowedValues: - dev - staging - prod Description: Environnement de déploiement Resources: # Stack imbriqué pour le réseau NetworkStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.amazonaws.com/my-templates/network.yaml Parameters: Environment: !Ref Environment VpcCidr: 10.0.0.0/16 # Stack imbriqué pour la base de données DatabaseStack: Type: AWS::CloudFormation::Stack Properties: TemplateURL: https://s3.amazonaws.com/my-templates/database.yaml Parameters: Environment: !Ref Environment DbInstanceClass: db.t3.small VpcId: !GetAtt NetworkStack.Outputs.VpcId SubnetIds: !GetAtt NetworkStack.Outputs.PrivateSubnetIds # Stack imbriqué pour l'application ApplicationStack: Type: AWS::CloudFormation::Stack DependsOn: - NetworkStack - DatabaseStack Properties: TemplateURL: https://s3.amazonaws.com/my-templates/application.yaml Parameters: Environment: !Ref Environment VpcId: !GetAtt NetworkStack.Outputs.VpcId SubnetIds: !GetAtt NetworkStack.Outputs.PublicSubnetIds DbEndpoint: !GetAtt DatabaseStack.Outputs.DbEndpoint DbName: !GetAtt DatabaseStack.Outputs.DbName Outputs: WebsiteUrl: Description: URL de l'application Value: !GetAtt ApplicationStack.Outputs.WebsiteUrl ApiEndpoint: Description: URL de l'API Value: !GetAtt ApplicationStack.Outputs.ApiEndpoint # Template réseau (network.yaml) AWSTemplateFormatVersion: '2010-09-09' Description: 'Template pour l''infrastructure réseau' Parameters: Environment: Type: String VpcCidr: Type: String Default: 10.0.0.0/16 Resources: # VPC VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidr EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub '${Environment}-vpc' # Sous-réseaux publics PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 6, 8]] AvailabilityZone: !Select [0, !GetAZs ''] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub '${Environment}-public-subnet-1' PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 6, 8]] AvailabilityZone: !Select [1, !GetAZs ''] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub '${Environment}-public-subnet-2' # Sous-réseaux privés PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !Select [2, !Cidr [!Ref VpcCidr, 6, 8]] AvailabilityZone: !Select [0, !GetAZs ''] Tags: - Key: Name Value: !Sub '${Environment}-private-subnet-1' PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !Select [3, !Cidr [!Ref VpcCidr, 6, 8]] AvailabilityZone: !Select [1, !GetAZs ''] Tags: - Key: Name Value: !Sub '${Environment}-private-subnet-2' # Autres ressources réseau (Internet Gateway, NAT Gateway, Route Tables, etc.) # ... Outputs: VpcId: Description: ID du VPC Value: !Ref VPC PublicSubnetIds: Description: Liste des IDs des sous-réseaux publics Value: !Join [',', [!Ref PublicSubnet1, !Ref PublicSubnet2]] PrivateSubnetIds: Description: Liste des IDs des sous-réseaux privés Value: !Join [',', [!Ref PrivateSubnet1, !Ref PrivateSubnet2]]

AWS CDK : CloudFormation en code

AWS CDK (Cloud Development Kit) permet de définir l'infrastructure en utilisant des langages comme Icône TypeScriptTypeScript ou Python, qui génèrent ensuite des templates CloudFormation :

infrastructure-stack.ts (AWS CDK)
// Exemple AWS CDK en TypeScript pour créer un stack CloudFormation import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as iam from 'aws-cdk-lib/aws-iam'; export class InfrastructureStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // Définir un VPC avec 2 sous-réseaux publics et 2 sous-réseaux privés const vpc = new ec2.Vpc(this, 'MainVpc', { maxAzs: 2, natGateways: 1, subnetConfiguration: [ { name: 'public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, }, { name: 'private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24, }, ], }); // Créer un groupe de sécurité pour les instances web const webSG = new ec2.SecurityGroup(this, 'WebSecurityGroup', { vpc, description: 'Allow HTTP and SSH', allowAllOutbound: true, }); webSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP traffic' ); webSG.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'Allow SSH traffic' ); // Créer un bucket S3 pour les assets statiques const staticBucket = new s3.Bucket(this, 'StaticAssetsBucket', { versioned: true, removalPolicy: cdk.RemovalPolicy.RETAIN, encryption: s3.BucketEncryption.S3_MANAGED, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, }); // Créer un rôle IAM pour les instances EC2 const webServerRole = new iam.Role(this, 'WebServerRole', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), ], }); // Donner accès au bucket S3 aux instances EC2 staticBucket.grantRead(webServerRole); // User data pour l'installation d'un serveur web const userData = ec2.UserData.forLinux(); userData.addCommands( 'yum update -y', 'yum install -y httpd', 'systemctl start httpd', 'systemctl enable httpd', 'echo "<html><body><h1>Hello from CDK!</h1></body></html>" > /var/www/html/index.html' ); // Créer une instance EC2 const webServer = new ec2.Instance(this, 'WebServer', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC, }, securityGroup: webSG, instanceType: ec2.InstanceType.of( ec2.InstanceClass.T3, ec2.InstanceSize.MICRO ), machineImage: ec2.MachineImage.latestAmazonLinux({ generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2, }), role: webServerRole, userData, }); // Outputs new cdk.CfnOutput(this, 'WebServerPublicIP', { value: webServer.instancePublicIp, description: 'The public IP address of the web server', }); new cdk.CfnOutput(this, 'BucketName', { value: staticBucket.bucketName, description: 'The name of the S3 bucket', }); new cdk.CfnOutput(this, 'WebServerURL', { value: `http://${webServer.instancePublicDnsName}`, description: 'URL of the web server', }); } }

Fonctionnalités avancées

  • Change Sets : Prévisualisation des modifications avant leur application
  • Drift Detection : Détection des modifications manuelles apportées aux ressources
  • Stack Policies : Protection des ressources critiques contre les mises à jour accidentelles
  • Custom Resources : Extension de CloudFormation pour gérer des ressources personnalisées
  • Macros : Transformation dynamique des templates pour générer du contenu
  • Provisioned Products : Intégration avec AWS Service Catalog pour une gestion standardisée

Bonnes pratiques

  • Validation : Valider les templates avec aws cloudformation validate-template ou cfn-lint
  • Stacks modulaires : Utiliser des stacks imbriqués pour séparer les préoccupations (réseau, sécurité, applications)
  • Gestion des paramètres : Utiliser AWS Systems Manager Parameter Store pour les paramètres sensibles
  • Dépendances explicites : Utiliser DependsOn pour contrôler l'ordre de création des ressources
  • Rollback sur échec : Configurer le rollback automatique en cas d'échec lors de la création ou mise à jour
  • DeletionPolicy : Protéger les données importantes avec la politique de suppression Retain ou Snapshot
  • Tests : Tester les templates dans des environnements de développement avant la production
Applications concrètes

Cas d'usage

Environnements multi-couches

Création d'environnements complets pour les applications, incluant le réseau, la sécurité, le calcul avec Icône EC2EC2, le stockage avec Icône S3S3 et les bases de données comme Icône DynamoDBDynamoDB, avec une cohérence totale entre les environnements.

Déploiement continu

Intégration avec les pipelines CI/CD comme Icône AWS CodePipelineAWS CodePipeline et Icône GitHub ActionsGitHub Actions pour automatiser le déploiement d'applications et d'infrastructures avec tests et validations automatisés.

Architectures standardisées

Création de templates standardisés pour les équipes afin d'assurer le respect des bonnes pratiques, des normes de sécurité et des processus de gouvernance de l'entreprise.

Infrastructure temporaire

Provisionnement rapide d'environnements de test ou de développement qui peuvent être facilement créés et détruits selon les besoins, optimisant les coûts et les ressources.

Industries et entreprises utilisant CloudFormation

CloudFormation est utilisé dans de nombreux secteurs pour automatiser et standardiser les infrastructures cloud :

Finance
Santé
Médias
SaaS
E-commerce
Jeux vidéo
IoT
Éducation