Le CTO de l’entreprise Cat’s eyes qui vous emploie est revenu à la charge alors que vous profitiez que le cloud gère le réseau à votre place, un donut et un café à la main. Bonne nouvelle ! Le CTO aime tellement votre travail qu’il voudrait que vous déployiez la même chose que tout à l’heure, mais dans un autre compte AWS, et si possible pour tout de suite parce qu’il a très envie de l’utiliser. Cela aurait été possible aussi si vous aviez automatisé la création de votre infrastructure, mais malheureusement cela n’a pas été le cas. Pas de panique ! Nous allons le faire dès à présent, et cela permettra de dupliquer à l’infini le réseau que nous avons créé.
Nous allons utiliser dans ce chapitre l’outil CloudFormation, qui est un outil AWS destiné à automatiser la création et la mise à jour de l’infrastructure AWS. CloudFormation ne pourra pas servir à automatiser autre chose que de l’infrastructure AWS : il n’est conçu que pour cela. Vous allez devoir détruire manuellement le VPC, ainsi que toutes les ressources que vous avez créées au chapitre précédent, car il n’est pas actuellement pas possible d’importer des éléments existants dans un template CloudFormation.
En bref, CloudFormation, c’est quoi ?
AWS CloudFormation :
fournit un langage pour décrire et provisionner toutes les ressources d'infrastructure dans AWS ;
vous permet d'utiliser un simple fichier texte pour modéliser et provisionner toutes les ressources nécessaires à vos applications, à travers toutes les régions et tous les comptes ;
gère pour vous les dépendances entre les différentes ressources. Par exemple, il comprendra de lui-même qu’il faut créer le VPC avant de tenter d’y attacher une passerelle de sortie.
Le fichier Template
CloudFormation se base sur un fichier descriptif de votre infrastructure : le fichier de template. Ce fichier de template peut être écrit au choix en JSON ou en YAML.
Créez votre premier VPC dans CloudFormation
Nous allons commencer par créer de nouveau notre VPC, mais avec CloudFormation. Il s’agit donc d’écrire des instructions dans notre template pour permettre de créer un VPC, comme si on l’avait fait dans la console web.
Pour créer un VPC, il convient d’écrire le code suivant :
AWSTemplateFormatVersion: 2010-09-09
Description: >-
AWS CloudFormation VPC Template
Resources:
MonSuperVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: 'MainVPC'
Hou là là, c’est quoi tous ces trucs !!
Tout va bien, ne vous en faites pas, je vais vous détailler son contenu ligne par ligne :
AWSTemplateFormatVersion: 2010-09-09 | Représente la version du langage que vous utilisez. Cela sera toujours la même chose, jusqu’à ce qu’une nouvelle version soit éditée par AWS. |
Description: >- AWS CloudFormation VPC Template | Ces instructions permettent de donner une description à votre template. Cela n’a d’importance que pour la documentation. |
Resources: | On déclare ici que les instructions qui vont suivre sont des ressources, c’est-à-dire des éléments à créer dans AWS. |
MonSuperVPC Type: 'AWS::EC2::VPC' | On déclare une ressource, dont le nom sera “MonSuperVPC”. Le type de la ressource AWS::EC2::VPC permet à CloudFormation de savoir quel élément il doit créer dans le cloud. Le nom de la ressource ne correspond pas au nom qui sera visible dans AWS, mais au nom que vous donnez à la ressource, pour pouvoir l’identifier par la suite dans votre stack. Nous reviendrons sur ce point plus tard. Notez ici les espaces que j’ai colorés en rouge au début des lignes, ils vous permettent de voir l’imbrication des éléments entre eux : VPC est une Resource et possède un Type. |
Properties: CidrBlock: 10.0.0.0/16 | Si vous avez suivi le chapitre précédent, vous aurez compris qu’ici on déclare les différentes propriétés de notre VPC, que nous avions renseignées tout à l’heure sur l’interface web, et en particulier le bloc CIDR du VPC : 10.0.0.0/16. Notez bien les espaces avant les mots clés : ici Properties est un niveau en dessous de VPC, et CidrBlock est un niveau en dessous de Properties. |
Tags: - Key: Name Value: 'MainVPC' | Enfin, nous déclarons un tag Name qui permet de donner un nom à notre VPC dans AWS. |
Ouf ! maintenant il faut tester notre template.
Exécutez votre premier template dans CloudFormation
Pour commencer, cliquez sur Services, et ouvrez CloudFormation dans la partie Management and Governance. L’interface de CloudFormation s’ouvre ; suivez alors les étapes suivantes :
Créez un fichier dans votre ordinateur, et nommez-le infrastructure.yml.
Insérez les éléments précédents dans le fichier.
Dans la console web d’AWS, cliquez sur Create Stack.
Choisissez Uploada template to Amazon S3.
Sélectionnez votre fichier, et cliquez sur Next.
Donnez un nom à votre stack, comme par exemple Network.
Cliquez sur Next.
Vous pouvez ignorer tous les autres paramètres pour le moment, et cliquer encore sur Next.
Enfin, cliquez sur Create.
Immédiatement, CloudFormation va tenter d’exécuter le contenu du fichier ; l’information CREATE_IN_PROGRESS est alors visible :
Une fois terminé, le message CREATE_COMPLETE s’affiche.
Vous pouvez jeter un coup d’œil aux événements en dessous pour voir comment CloudFormation a réagi face à votre template :
Si vous allez jeter un œil à la section VPC, vous constaterez que celui-ci est bel et bien créé :
C’est cool, non ? Et c’est pas fini. On va s’occuper des sous-réseaux !
Créez les sous-réseaux dans CloudFormation
Souvenez-vous, nous avions créé :
un sous-réseau public dans la zone de disponibilité A ;
un sous-réseau public dans la zone de disponibilité B ;
Un sous-réseau privé dans la zone de disponibilité A ;
Un sous-réseau privé dans la zone de disponibilité B.
Un coup d’œil à la documentation CloudFormation sur les sous-réseaux, et on ajoute à notre template l’élément suivant, qui permet de créer notre premier sous-réseau privé dans la zone A :
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.0.0/24
Tags:
- Key: Name
Value: 'MainVPC-private-a'
VpcId: ???
Nous avons ici un problème (ligne 9) : il faudrait mettre l’identifiant du VPC pour l’associer au sous-réseau, mais celui-ci n’existe pas encore, car c’est notre template qui va le créer !
Ça veut dire qu’il faut faire plusieurs templates ?
Heureusement, non ! Vous souvenez-vous quand je vous ai dit qu’on reviendrait sur cette histoire de nom des ressources ? C’est maintenant !
Nous allons en effet nous servir du nom de la ressource VPC dans le template, pour indiquer à CloudFormation d’aller chercher l’identifiant du VPC après sa création, de manière à ce qu’il l’injecte dans le sous-réseau. Ce n’est pas clair ? Regardez :
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.0.0/24
Tags:
- Key: Name
Value: 'MainVPC-private-a'
VpcId: !Ref MonSuperVPC
Ici, en indiquant !Ref MonSuperVPC, je fais référence à la ressource qui se trouve un peu avant dans mon template. CloudFormation va ainsi naturellement gérer les dépendances et créer le VPC avant de tenter de créer les sous-réseaux, et au moment de créer les sous-réseaux, il utilisera le numéro du VPC créé précédemment. Simple, non ?
Voici le code complet :
AWSTemplateFormatVersion: 2010-09-09
Description: >-
AWS CloudFormation VPC Template
Resources:
MonSuperVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: 10.0.0.0/16
Tags:
- Key: Name
Value: 'MainVPC'
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: 10.0.0.0/24
Tags:
- Key: Name
Value: 'MainVPC-private-a'
VpcId: !Ref MonSuperVPC
On peut à présent mettre à jour notre template et essayer de voir si notre sous-réseau est correctement créé. Cliquez sur le nom du template, et cliquez en haut à droite sur Update Stack (ou Action puis Update Stack dans la nouvelle interface). Comme précédemment, choisissez Upload a template to Amazon S3 et choisissez votre fichier. Enfin, cliquez sur Next jusqu’au bout des fenêtres de dialogue.
CloudFormation vous donne un aperçu des changements, et vous pouvez cliquer sur Update.
La stack passe en UPDATE_IN_PROGRESS et enfin UPDATE_COMPLETE. Votre sous-réseau est créé !
On constate également que le sous-réseau est créé :
Il ne vous reste plus qu’à ajouter les autres sous-réseaux dans le template !
Pour aller plus loin : calculez automatiquement les blocs CIDR
Je vais vous faire un aveu, pour moi les blocs CIDR ça coule pas de source. Heureusement, il existe une fonction de feignant comme je les aime, pour les calculer automatiquement ! En effet, CloudFormation fournit un certain nombre d’instructions pour simplifier la rédaction de tâches rébarbatives, comme calculer des CIDR. Il s’agit de l’instruction Fn::Cidr.
Voici comment elle s’utilise :
Fn::Cidr:
- ipBlock
- count
- cidrBits
ou dans sa forme abrégée : !Cidr [ ipBlock, count, cidrBits ].
Par exemple, pour calculer automatiquement le CIDR 10.0.0.0/24 du sous-réseau privé A, j’aurais pu écrire :
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: !Select [ 0, !Cidr [ 10.0.0.0/16, 1, 8 ]]
Tags:
- Key: Name
Value: 'MainVPC-private-a'
VpcId: !Ref MonSuperVPC
Ici, l’instruction est donnée de découper le CIDR du VPC 10.0.0.0/16 en un sous-bloc de 8 bits. L’instruction !Cidr renvoyant un tableau d’éléments, on a ajouté l’instruction !Select qui permet de récupérer le premier élément généré (dont l’index est 0).
Encore plus fort, je ne suis pas obligé de réécrire 10.0.0.0/16, puisque je l’ai déjà écrit dans le VPC. Je peux demander à CloudFormation d’aller chercher le bloc CIDR du VPC et de l’utiliser directement :
PrivateSubnetA:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: eu-west-1a
CidrBlock: !Select [ 0, !Cidr [ !GetAtt MonSuperVPC.CidrBlock, 1, 8 ]]
Tags:
- Key: Name
Value: 'MainVPC-private-a'
VpcId: !Ref MonSuperVPC
L’instruction GetAtt permet de récupérer un attribut d’une ressource CloudFormation existante ; en l'occurrence dans cet exemple, l’attribut CidrBlock de la ressource VPC.
Cela fait partie des éléments un peu plus avancés que vous pourrez retrouver dans les templates CloudFormation professionnels, mais ce n’est pas très grave si vous ne les maîtrisez pas pour le moment ! Il est temps de passer à la NAT Gateway, mais avant elle, aux IP élastiques.
Créez la passerelle de sortie Internet dans CloudFormation
Afin de pouvoir rendre deux de nos sous-réseaux publics, nous avons besoin d’une passerelle de sortie. En suivant la documentation AWS, on écrit donc ceci :
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: 'MainVPC-InternetGateway'
Comme précédemment, attention aux espaces en début de ligne ! On donne ici le nom “MainVPC-InternetGateway” à notre passerelle de sortie, mais vous pouvez lui donner le nom que vous voulez.
Testez votre stack, et allez voir la passerelle, vous constatez qu’elle est détachée ! En effet, vous n’avez pas indiqué à CloudFormation de l’attacher à un VPC.
On ajoute alors les instructions :
AttachInternetGateway:
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref MonSuperVPC
InternetGatewayId: !Ref InternetGateway
Comme précédemment, attention aux espaces en début de ligne ! Vous constatez qu’on utilise les références des ressources créées précédemment.
Mais pourquoi a-t-on donné le nom AttachInternetGateway à cette instruction, alors que ce n’est pas une création de ressource ?
C’est une très bonne question. Dans CloudFormation, toutes les actions que vous réaliserez dans le compte AWS sont des ressources, y compris les actions pour attacher des éléments entre eux. Par conséquent, vous êtes obligé de lui donner un nom dans le template, même si cela ne correspond pas à un “objet AWS” à créer. Ici, nous l’avons appelé AttachInternetGateway, mais on aurait pu mettre n’importe quoi.
Testez votre stack en re-uploadant le nouveau template, et on continue !
Créez des adresses IP élastiques dans CloudFormation
Souvenez-vous : nous avions dû créer deux adresses pour nos deux passerelles NAT. D’après la documentation de CloudFormation, vous pouvez créer des adresses IP élastiques à l’aide du type AWS::EC2::EIP ; on écrit donc le code suivant :
NatEipA:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatEipB:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Comme précédemment, attention aux espaces en début de ligne ! La valeur de la propriété Domain sera toujours vpc.
Nous avons à présent les instructions pour créer nos deux IP élastiques. Testez le déploiement de votre stack, et on continue avec les passerelles NAT.
Créez les passerelles NAT dans CloudFormation
C’est parti, on retourne dans la documentation de CloudFormation, cette fois-ci sur les passerelles NAT, et on écrit :
NatGatewayA:
DependsOn: AttachInternetGateway
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatEipA.AllocationId
SubnetId: !Ref PublicSubnetA
Tags:
- Key: Name
Value: MainVPC-nat-a
Comme précédemment, attention aux espaces en début de ligne ! Nous créons ainsi une passerelle NAT dans le sous-réseau public A à l’aide de sa référence récupérée avec Ref, et nous utilisons GetAtt pour récupérer le numéro d’allocation de l’IP élastique, pour que la passerelle NAT se l’approprie.
Hé, stop ! C’est quoi cette nouvelle instruction DependsOn ?
Je vous avais dit qu’en utilisant les références à l’aide de l’attribut Ref, CloudFormation allait déterminer dans quel ordre il fallait créer les ressources. Parfois, vous allez vouloir créer des ressources avant d’autres, mais sans que celles-ci soient liées, car il n’y aura pas de référence entre elles. C’est le cas de figure ici.
Pour créer une passerelle NAT dans un VPC, vous devez avoir les prérequis suivants :
un sous-réseau ;
une IP élastique ;
une passerelle Internet attachée au VPC.
Parce que nous faisons référence au sous-réseau et à l’IP élastique, CloudFormation sait qu’il faut les créer avant de tenter de construire la passerelle NAT.
La solution : indiquer à CloudFormation explicitement d’attendre que l’attachement à la passerelle Internet ait été effectué, à l’aide de l’instruction DependsOn.
Testez le code que je vous ai donné, et tentez d’écrire seul la seconde passerelle NAT dans le sous-réseau public B. Une fois terminé, nous configurerons le routage !
Créez les tables de routage dans CloudFormation
On a encore un gros morceau à écrire, et puis ça sera terminé ! Souvenez-vous, nous devons configurer le routage de la manière suivante :
Cela correspond aux ressources suivantes :
une table de routage pour le sous-réseau privé de la zone A, pointant sur la passerelle NAT de la zone A ;
une table de routage pour le sous-réseau privé de la zone B, pointant sur la passerelle NAT de la zone B ;
une table de routage pour le sous-réseau public de la zone A, pointant sur la passerelle Internet ;
une table de routage pour le sous-réseau public de la zone B, pointant sur la passerelle Internet.
Il faut donc créer 4 tables de routage, attachées aux bons sous-réseaux et avec les instructions correspondantes à l’intérieur.
Pour créer une table de routage, les commandes sont assez simples ; ici, pour le sous-réseau public A :
PublicRouteTableA:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref MonSuperVPC
Tags:
- Key: Name
Value: MainVPC-route-public-b
Mais on a pas renseigné le sous-réseau à attacher ! Il faut le faire, non ?
Exact ! Cette information nécessite l’utilisation d’une autre ressource, AWS::EC2::SubnetRouteTableAssociation, comme ceci :
AttachPublicRouteTableA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTableA
SubnetId: !Ref PublicSubnetA
Et les instructions de routage, alors ?!
C’est encore une autre ressource. Ici, nous créons une route vers la passerelle de sortie à la table de routage du sous-réseau privé A :
PublicRouteA:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
RouteTableId: !Ref PublicRouteTableA
Je vous laisse faire seul la suite, c’est-à-dire la création des autres tables de routage et leur pointage. Attention, les tables de routage privées doivent pointer sur les passerelles NAT correspondant à leur sous-réseau !
C’est fini ! À présent, regardons si nous pouvons optimiser le contenu du fichier de template.
Ajoutez des paramètres en entrée de votre stack
Quand on crée un template CloudFormation, on a souvent envie que ce soit quelque chose de complètement générique. C’est-à-dire, qu’on puisse l’utiliser plusieurs fois, en changeant juste quelques paramètres. Jusqu’à présent, pour changer le bloc d’adresses du VPC, il faut modifier le fichier, mais cela serait tout de même plus pratique d’avoir simplement à les donner à CloudFormation à l’exécution, sans avoir à changer le fichier.
Heureusement, CloudFormation est capable de transmettre des variables à notre template, et pour cela, il faut indiquer dans le fichier qu’on attend des Parameters. Par exemple :
Parameters:
VpcCidrBlock:
Type: String
Default: 10.0.0.0/16
Description: Enter the main VPC CIDR block.
On a déclaré ci-dessus que le template attend un paramètre en entrée, appelé VpcCidrBlock. Ce paramètre est une variable qui sera spécifiée par l’utilisateur à l’exécution du template. Pour pouvoir utiliser la valeur indiquée par l’utilisateur, on utilise alors l’attribut Ref comme ceci :
MonSuperVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: !Ref VpcCidrBlock
Lors de l’exécution de notre stack, CloudFormation nous propose de renseigner cette valeur :
En changeant la valeur, CloudFormation met à jour le VPC et ses ressources associées.
Décryptez les problèmes qui peuvent se produire
Parfois, il se peut que la création de votre stack se passe mal. Dans un tel cas de figure, CloudFormation vous indiquera un code d’erreur ; en voici quelques-uns :
Code | Description |
CREATE_FAILED | Échec de la création d'une ou de plusieurs stacks. |
DELETE_FAILED | Échec de la destruction d'une ou de plusieurs stacks. |
ROLLBACK_FAILED | Échec de la suppression d'une ou de plusieurs stacks après un échec de création ou après l'annulation explicite de la création de la stack. |
UPDATE_ROLLBACK_FAILED | Échec de la restauration de l'état de fonctionnement d'une ou plusieurs stacks, après un échec de mise à jour. |
En résumé
CloudFormation est un outil d’Infrastructure as Code ;
CloudFormation supporte les langages YAML et JSON ;
toutes les ressources possibles en JSON avec CloudFormation sont également possibles en YAML ;
un template CloudFormation doit comporter dans l’en-tête l’instruction AWSTemplateFormatVersion: 2010-09-09 ;
les ressources CloudFormation peuvent être référencées entre elles avec le mot clé Ref ;
vous pouvez récupérer des attributs au sein de votre stack avec le mot clé GetAtt ;
vous pouvez déclarer des dépendances explicites avec le mot clé DependsOn.