AWS CDK : Cloud Development Kit

Posted on November 1, 2019 | 17 minute read

Comme ceux qui me suivent le savent sans doute, je m’intéresse énormément au domaine de l’industrialisation et de l’automatisation. Un sous-ensemble de ce domaine est l’IAC, ou Infrastructure As Code. Toujours pas une surprise pour ceux qui me connaissent un peu, je suis un grand fanboy de l’univers AWS, Amazon Web Services.

Et ça tombe bien car dans cet univers, un petit nouveau vient de montrer le bout de son nez, j’ai nommé : AWS CDK ou Cloud Development Kit. Dans cet article (et comme promis dans l’édito précédent, mouahaha, je suis un homme de confiance), je vais vous faire un retour de mon premier contact avec cette merveille. Oui, j’ai vraiment aimé ça. Et j’espère aussi que ça vous aidera à mieux conceptualiser ce que c’est et …

D’ailleurs, à quoi ça sert ? Le postulat de base est simple. Habituellement, quand on développe une application et qu’on désire faire l’infrastructure As Code, on créé un repository différent (ou un sous-répertoire dans le projet) où l’on entrepose le code de l’infrastructure voulue.

Le plus souvent (NON, je ne veux pas lancer de troll), ce code est du code Terraform, parce que ça a grave la hype en ce moment et que ça peut bosser avec énormément de types d’infrastructures et de clouds, grâce à des plugins appelés providers, certains officiels et d’autres non. Bref, là où je veux en venir c’est que le code Terrafom dispose d’une syntaxe bien à lui.

Dans le cas d’AWS, on peut utiliser CloudFormation. C’est une sorte de moteur d’orchestration qui va manger du YAML respectant certains standards définis par Amazon, et après digestion, pondre une infrastructure. Vous avez l’image ? Bien. Il suffit donc de “décrire” l’infra en YAML puis de la pousser via une stack: cela reste donc de l’IAC.

Et qu’est-ce que ces deux exemples ont en commun ? Imaginez. Vous êtes en train de développer une motherf…ing (n’ayons pas peur des mots) application en NodeJS, et vous développez en même temps l’infrastructure. Ah c’est super pénible, il vous faut changer de langage/de techno en cours de développement, sauter de Terraform à NodeJS, puis revenir en sens inverse.

Bingo ! AWS CDK est donc un kit complet qui permet d’écrire votre infrastructure en Python, C#, Javascript ou enfin Typescript, et par là même vous permet d’écrire tout votre projet dans le même langage (si votre projet est en brainfuck, ça vous regarde, hein).

Tout ça au travers d’un workflow hyper smoothie, n’ayons pas peur des mots, que je vais donc décrire dans cet article. Et je vais le faire en Javascript, parce qu’en ce moment je cherche à progresser dans ce langage: une pierre, deux coups, tout ça…

Partie I : installation

Je commencerais par le début, ce qui est souvent une bonne idée : l’installation et le paramétrage des outils.

J’ai besoin en premier lieu d’un utilisateur IAM qui dispose de la policy AdministratorAccess. Rien de bien compliqué. Puis caler ses credentials (access key, secret key) dans mon .aws/credentials, via aws configure ou non. En terme de prérequis, il est à noter que CDK nécessite NodeJS en version supérieure à 8.12.0.

L’installation de l’outil principal ne casse pas des barres, comme souvent avec npm :

$ sudo npm install -g aws-cdk 
/usr/local/bin/cdk -> /usr/local/lib/node_modules/aws-cdk/bin/cdk

> core-js@2.6.10 postinstall /usr/local/lib/node_modules/aws-cdk/node_modules/core-js
> node postinstall || echo "ignore"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 
> https://opencollective.com/core-js 
> https://www.patreon.com/zloirock 

Also, the author of core-js ( https://github.com/zloirock ) is looking for a good job -)

+ aws-cdk@1.15.0
added 237 packages from 218 contributors in 13.554s
$ cdk --version
1.15.0 (build bdbe3aa)

Ready to go.

On notera au passage l’auteur de core-js qui se fait un peu de pub et cherche du taff, mais bon, l’open source c’est aussi fait pour ça.

Partie II : les bases et le workflow

CDK fonctionne avec la commande du même nom et propose plusieurs sous-commandes permettant de travailler avec le workflow. En toute logique, la première étape est l’initialisation de mon environnement et de ma nouvelle infrastructure, et cela se fait via la sous-commande init :

$ cdk init
Available templates:
* app: Template for a CDK Application
   └─ cdk init app --language=[csharp|fsharp|java|javascript|python|typescript]
* lib: Template for a CDK Construct Library
   └─ cdk init lib --language=typescript
* sample-app: Example CDK Application with some constructs
   └─ cdk init sample-app --language=[csharp|javascript|python|typescript]

Elle m’indique qu’il y a plusieurs types de templates possibles. Pour cette partie de l’article, je vais bosser avec une sample-app, en Javascript.

$ cdk init sample-app --language javascript
Applying project template sample-app for javascript
Initializing a new git repository...
Executing npm install...

# Useful commands

 * `npm run test`         perform the jest unit tests
 * `cdk deploy`           deploy this stack to your default AWS account/region
 * `cdk diff`             compare deployed stack with current state
 * `cdk synth`            emits the synthesized CloudFormation template                   

Je remarque que ça a transformé mon répertoire en repository git et que je me retrouve directement dans une branche master.

git branch master

Un cdk diff me montre qu’il se passe déjà des choses :

$ cdk diff
Stack CdkworkStack
IAM Statement Changes
┌───┬─────────────────────┬────────┬─────────────────┬───────────────────────────────────────┬───────────────────────────────────────┐
│   │ Resource            │ Effect │ Action          │ Principal                             │ Condition                             │
├───┼─────────────────────┼────────┼─────────────────┼───────────────────────────────────────┼───────────────────────────────────────┤
│ + │ ${CdkworkQueue.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com             │ "ArnEquals": {                        │
│   │                     │        │                 │                                       │   "aws:SourceArn": "${CdkworkTopic}"  │
│   │                     │        │                 │                                       │ }                                     │
└───┴─────────────────────┴────────┴─────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Conditions
[+] Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}

Resources
[+] AWS::SQS::Queue CdkworkQueue CdkworkQueue1D9D7409 
[+] AWS::SQS::QueuePolicy CdkworkQueue/Policy CdkworkQueuePolicy0E0C1E5D 
[+] AWS::SNS::Subscription CdkworkQueue/CdkworkStackCdkworkTopic15D2338C CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E 
[+] AWS::SNS::Topic CdkworkTopic CdkworkTopic97DA4830 

Mais j’y reviendrais.

Dans les commandes exposées après le cdk init de tout à l’heure, j’ai vu un qu’un script watch avait également été créé. En fait, ça affiche en temps réel les changements faits à l’infrastructure, je peux donc garder un terminal ouvert dessus pour voir évoluer la stack en cours de build, via un npm run watch:

npm run watch

A ce stade, mon répertoire de travail ressemble à ça :

cdkwork dir

Voici la description des fichiers principaux, de ce que j’ai compris :

Le fichier cdkwork.js contient effectivement du code très simple :

#!/usr/bin/env node
const cdk = require('@aws-cdk/core');
const { CdkworkStack } = require('../lib/cdkwork-stack');

const app = new cdk.App();
new CdkworkStack(app, 'CdkworkStack');

Je ne suis pas encore un master en Javascript, mais je ne pense pas trop m’avancer en expliquant que ça va simplement chercher la classe principale de ma lib cdkwork-stack.js, elle même héritant de la classe cdk.Stack qui elle provient de cdk. Ensuite, ça en récupère les directives pour créer un objet app directement issu de cdk. Finalement, ça lance la stack en combinant les deux : une app CDK qui utilise en argument ma classe description de stack.

A ce stade, j’ai une meilleure idée de comment fonctionne le kit.

Je continue. Juste après un cdk init, mon fichier lib/cdkwork-stack.js contient :

const sns = require('@aws-cdk/aws-sns');
const subs = require('@aws-cdk/aws-sns-subscriptions');
const sqs = require('@aws-cdk/aws-sqs');
const cdk = require('@aws-cdk/core');

class CdkworkStack extends cdk.Stack {
  /**
   * @param {cdk.App} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    const queue = new sqs.Queue(this, 'CdkworkQueue', {
      visibilityTimeout: cdk.Duration.seconds(300)
    });

    const topic = new sns.Topic(this, 'CdkworkTopic');

    topic.addSubscription(new subs.SqsSubscription(queue));
  }
}

module.exports = { CdkworkStack }

Voilà qui explique le résultat de mon cdk diff de tout à l’heure, le sample créé dès le départ une queue SQS ainsi qu’un topic SNS pour s’y abonner et diffuser les messages de ladite queue. Ce n’est pas le cas lorsqu’on créé une app vierge, je rappelle qu’il s’agit là du sample.

Une autre commande de base du toolkit est la commande synth. En gros, CDK ne fait que produire du code CloudFormation, et dans le jargon de l’outil, on dit qu’il “synthétise”. La commande cdk synth produit donc le code CloudFormation résultant du code Javascript.

Dans les commandes de base, il n’en reste que 3 que je n’ai pas abordé. La première est cdk bootstrap. Si je devais décrire son comportement, je dirais que ça va créer les éléments de base dans le compte AWS pour que l’on puisse utiliser CDK, en l’occurence un bucket S3 qui va contenir les changesets de la stack CloudFormation pendant le processus de déploiement, ainsi qu’une stack CloudFormation (nommée CDKToolkit) qui servira d’exécutante pour le code CloudFormation produit par CDK.

$ cdk bootstrap
 ⏳  Bootstrapping environment aws://829937339934/eu-west-1...
CDKToolkit: creating CloudFormation changeset...
 1/2 | 12:04:02 | CREATE_COMPLETE      | AWS::S3::Bucket | StagingBucket 
 2/2 | 12:04:04 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CDKToolkit 
 ✅  Environment aws://829937339934/eu-west-1 bootstrapped.

En cela, la commande bootstrap ne doit être utilisée qu’une seule fois, à la création du projet. Une fois ces éléments en place, ils sont utilisés pour faire fonctionner tout le workflow. Ils ne font donc pas partie de mon code, d’ailleurs mon fichier lib/cdkwork-stack.js n’a pas été impacté par ce bootstrap et n’a pas évolué.

J’ai effectivement de nouvelles ressources (mon s3 aws-trail n’en fait pas partie), un bucket et une stack CloudFormation :

bucket s3

stack cdktoolkit

Pour déployer mon code contenant mon infrastructure, cela va donc passer par ces deux éléments. Une fois que j’ai écrit mon code, je lance une nouvelle commande : cdk deploy. Cette dernière lance le workflow:

Pour revenir à la première commande que j’ai lancé, cdk diff : en fait elle ne montre pas ce qui a été créé, mais bien ce qui va être créé. Elle montre donc un diff entre ce qui existe sur mon infrastructure et le code trouvé en local dans mon projet. Ce que je veux dire, c’est que la stack SNS/SQS vue tout à l’heure n’est en fait pas créée tant que je n’ai pas fait mon cdk deploy! Let’s see :

$ cdk deploy
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬─────────────────────┬────────┬─────────────────┬───────────────────────────────────────┬───────────────────────────────────────┐
│   │ Resource            │ Effect │ Action          │ Principal                             │ Condition                             │
├───┼─────────────────────┼────────┼─────────────────┼───────────────────────────────────────┼───────────────────────────────────────┤
│ + │ ${CdkworkQueue.Arn} │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com             │ "ArnEquals": {                        │
│   │                     │        │                 │                                       │   "aws:SourceArn": "${CdkworkTopic}"  │
│   │                     │        │                 │                                       │ }                                     │
└───┴─────────────────────┴────────┴─────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Do you wish to deploy these changes (y/n)? y
CdkworkStack: deploying...
CdkworkStack: creating CloudFormation changeset...
 0/6 | 12:06:57 | CREATE_IN_PROGRESS   | AWS::CDK::Metadata     | CDKMetadata Resource creation Initiated
 1/6 | 12:06:58 | CREATE_COMPLETE      | AWS::CDK::Metadata     | CDKMetadata 
 2/6 | 12:07:07 | CREATE_COMPLETE      | AWS::SNS::Topic        | CdkworkTopic (CdkworkTopic97DA4830) 
 2/6 | 12:07:09 | CREATE_IN_PROGRESS   | AWS::SQS::QueuePolicy  | CdkworkQueue/Policy (CdkworkQueuePolicy0E0C1E5D) 
 2/6 | 12:07:09 | CREATE_IN_PROGRESS   | AWS::SQS::QueuePolicy  | CdkworkQueue/Policy (CdkworkQueuePolicy0E0C1E5D) Resource creation Initiated
 3/6 | 12:07:09 | CREATE_COMPLETE      | AWS::SQS::QueuePolicy  | CdkworkQueue/Policy (CdkworkQueuePolicy0E0C1E5D) 
 3/6 | 12:07:10 | CREATE_IN_PROGRESS   | AWS::SNS::Subscription | CdkworkQueue/CdkworkStackCdkworkTopic15D2338C (CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E) 
 3/6 | 12:07:11 | CREATE_IN_PROGRESS   | AWS::SNS::Subscription | CdkworkQueue/CdkworkStackCdkworkTopic15D2338C (CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E) Resource creation Initiated
 4/6 | 12:07:11 | CREATE_COMPLETE      | AWS::SNS::Subscription | CdkworkQueue/CdkworkStackCdkworkTopic15D2338C (CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E) 
 5/6 | 12:07:12 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CdkworkStack 

 ✅  CdkworkStack

Stack ARN:
arn:aws:cloudformation:eu-west-1:829937339934:stack/CdkworkStack/b29ebec0-fc97-11e9-ae77-0646713dba72

Là, mon diff devient vide :

$ cdk diff
Stack CdkworkStack
There were no differences

Et mes ressources sont créées. Ma file SQS :

Queue SQS

Et le topic SNS :

SNS topic

Je trouve ça brillant, on ne va pas se mentir.

Avant d’attaquer du vrai code, reste une question : comment nettoyer ma stack avant d’en créer une nouvelle ? Hé bien, ça aussi, c’est brillant : je dois seulement modifier mon code dans lib/cdkwork-stack.js (le fichier décrivant toute la stack, vous suivez?), qui devient :

const cdk = require('@aws-cdk/core');

class CdkworkStack extends cdk.Stack {
  /**
   * @param {cdk.App} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);
  }
}

module.exports = { CdkworkStack }

Autrement dit, je l’ai vidé de ses deux ressources créées :

const queue = new sqs.Queue(this, 'CdkworkQueue', {
    visibilityTimeout: cdk.Duration.seconds(300)
});

const topic = new sns.Topic(this, 'CdkworkTopic');
topic.addSubscription(new subs.SqsSubscription(queue));

Ainsi que des trois inclusions désormais inutiles, mon code ne créeant plus de ressources SNS ou de SQS :

const sns = require('@aws-cdk/aws-sns');
const subs = require('@aws-cdk/aws-sns-subscriptions');
const sqs = require('@aws-cdk/aws-sqs');

En effet, la stack principale se compose de CDK mais en modulaire, ce qui veut dire que j’inclus après chaque sous-module que je désire exploiter. Ce serait bien trop lourd de tout charger en monolithique pour en utiliser qu’un sous-ensemble, ce serait une perte inutile, notamment en termes de chargement d’objets en mémoire.

Il faut avouer que le code est carrément lisible. Une fois cela fait, mon diff présente des modifications, en toute logique, puisque j’ai modifié ma stack mais je ne l’ai pas encore déployée :

$ cdk diff
Stack CdkworkStack
IAM Statement Changes
┌───┬─────────────────────────────────┬────────┬─────────────────┬─────────────────────────────────┬─────────────────────────────────┐
│   │ Resource                        │ Effect │ Action          │ Principal                       │ Condition                       │
├───┼─────────────────────────────────┼────────┼─────────────────┼─────────────────────────────────┼─────────────────────────────────┤
│ - │ ${CdkworkQueue1D9D7409.Arn}     │ Allow  │ sqs:SendMessage │ Service:sns.amazonaws.com       │ "ArnEquals": {                  │
│   │                                 │        │                 │                                 │   "aws:SourceArn": "${CdkworkTo │
│   │                                 │        │                 │                                 │ pic97DA4830}"                   │
│   │                                 │        │                 │                                 │ }                               │
└───┴─────────────────────────────────┴────────┴─────────────────┴─────────────────────────────────┴─────────────────────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)

Resources
[-] AWS::SQS::Queue CdkworkQueue1D9D7409 destroy
[-] AWS::SQS::QueuePolicy CdkworkQueuePolicy0E0C1E5D destroy
[-] AWS::SNS::Subscription CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E destroy
[-] AWS::SNS::Topic CdkworkTopic97DA4830 destroy

Let’s destroy :

$ cdk deploy
CdkworkStack: deploying...
CdkworkStack: creating CloudFormation changeset...
 1/6 | 12:15:35 | UPDATE_COMPLETE      | AWS::CDK::Metadata | CDKMetadata 
 1/6 | 12:15:37 | UPDATE_COMPLETE_CLEA | AWS::CloudFormation::Stack | CdkworkStack 
 1/6 | 12:15:38 | DELETE_IN_PROGRESS   | AWS::SQS::QueuePolicy | CdkworkQueuePolicy0E0C1E5D 
 1/6 | 12:15:38 | DELETE_IN_PROGRESS   | AWS::SNS::Subscription | CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E 
 2/6 | 12:15:38 | DELETE_COMPLETE      | AWS::SQS::QueuePolicy | CdkworkQueuePolicy0E0C1E5D 
 3/6 | 12:15:39 | DELETE_COMPLETE      | AWS::SNS::Subscription | CdkworkQueueCdkworkStackCdkworkTopic15D2338C9A6FBA1E 
 3/6 | 12:15:39 | DELETE_IN_PROGRESS   | AWS::SNS::Topic    | CdkworkTopic97DA4830 
 3/6 | 12:15:39 | DELETE_IN_PROGRESS   | AWS::SQS::Queue    | CdkworkQueue1D9D7409 
 4/6 | 12:15:40 | DELETE_COMPLETE      | AWS::SNS::Topic    | CdkworkTopic97DA4830 
4/6 Currently in progress: CdkworkStack, CdkworkQueue1D9D7409

 ✅  CdkworkStack

Stack ARN:
arn:aws:cloudformation:eu-west-1:829937339934:stack/CdkworkStack/b29ebec0-fc97-11e9-ae77-0646713dba72

Je trouve ça vraiment trop cool.

Ah mais attendez, ça n’a pas tout détruit. En fait, le deploy ne détruit que les ressources créées par le code mais si vous vous souvenez bien, j’ai deux ressources qui ne font pas partie de mon code mais pourtant de ma stack CDK : mon bucket s3 et ma stack CloudFormation.

Ça, ça se détruit avec la commande bien nommée destroy :

$ cdk list    
CdkworkStack
$ cdk destroy CdkworkStack
Are you sure you want to delete: CdkworkStack (y/n)? y
CdkworkStack: destroying...

 ✅  CdkworkStack: destroyed

Nouvelle commande au passage, list, car CDK peut bosser sur plusieurs stack à la fois. Je n’ai pas encore trop compris comment, je ne l’aborderais donc pas.

Bon, je crois que j’ai fait un bon tour des bases pour utiliser CDK:

Maintenant, j’enchaîne sur un cas concret et non plus une sample-app.

Partie III : du code, servante, que j’build à foison !

Je me suis marré quand j’ai écrit le titre de cette partie. Pour ceux qui n’auraient pas la référence, shame on you.

Je vais créer une toute nouvelle stack pour cette partie. J’utilise une commande init avec des options différentes car je n’utilise plus une stack d’exemple:

$ cdk init app --language javascript 
Applying project template app for javascript
Initializing a new git repository...
Executing npm install...

# Useful commands

 * `npm run test`         perform the jest unit tests
 * `cdk deploy`           deploy this stack to your default AWS account/region
 * `cdk diff`             compare deployed stack with current state
 * `cdk synth`            emits the synthesized CloudFormation template

$ cdk bootstrap
 ⏳  Bootstrapping environment aws://829937339934/eu-west-1...
CDKToolkit: creating CloudFormation changeset...
 1/2 | 13:22:42 | CREATE_COMPLETE      | AWS::S3::Bucket | StagingBucket 
 2/2 | 13:22:43 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CDKToolkit 
 ✅  Environment aws://829937339934/eu-west-1 bootstrapped.

Tout est identique à l’app d’exemple, sauf que le code contenu dans lib/cdkwork-stack.js est vide de toute ressource :

const cdk = require('@aws-cdk/core');

class CdkworkStack extends cdk.Stack {
  /**
   *
   * @param {cdk.Construct} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    // The code that defines your stack goes here
  }
}

module.exports = { CdkworkStack }

Et le reste de la structure du projet est la même que montrée précédemment :

workdir

Pour faire du concret, je vais créer différentes ressources qui ne formeront pas un ensemble cohérent mais ça vous donnera un aperçu des possibilités :

Tout ça en m’aidant de l’API Reference de CDK.

III.A : le repository CodeCommit

Ici, je créé un repository Codecommit nommé examples-js-cdk et lui donne une description. Je créé également un topic SNS dans lequel il va publier un message à chaque commit sur la branche master afin de suivre les évolutions de mon repository.

const cdk = require('@aws-cdk/core');
const s3 = require('@aws-cdk/aws-codecommit');
const sns = require('@aws-cdk/aws-sns')

class CdkworkStack extends cdk.Stack {
  /**
   *
   * @param {cdk.Construct} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    const topic = new sns.Topic(this, 'examples-js-cdk-codecommit-topic');
    const repo = new codecommit.Repository(this, 'Repository', {
        repositoryName: 'examples-js-cdk',
        description: 'Some easy examples with AWS CDK',
    });
    repo.notify(topic);
    repo.onCommit('CommitToMaster', {
        target: new targets.SnsTopic(topic),
    });
  }
}

module.exports = { CdkworkStack }

III.B : le sous-domaine dans Route53

Rien de bien compliqué ici, je créé un sous-domaine cdk-tests.kharec.info, de type A, pointant sur l’ip 98.4.23.1.

const cdk = require('@aws-cdk/core');
const route53 = require('@aws-cdk/aws-route53');

class CdkworkStack extends cdk.Stack {
  /**
   *
   * @param {cdk.Construct} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    const ipDst = "98.4.23.1";

    const zoneKharecInfo = new route53.PublicHostedZone(this, 'HostedZone', {
      zoneName: 'kharec.info',
    });

    const record = new route53.ARecord(this, 'cdk-tests', {
      zone: zoneKharecInfo,
      target: route53.AddressRecordTarget.fromIpAddresses(ipDst),
      ttl: cdk.Duration.seconds(900),
    });
  }
};

module.exports = { CdkworkStack }

Le TTL par défaut est à 30 minutes. Mais je peux modifier l’objet route53.ARecord pour y ajouter une propriété de type Duration, type lui-même contenu dans @aws-cdk/core, ici pour y ajouter un TTL à 900 secondes.

III.C : le bucket S3

Sans nul doute le plus simple.

const cdk = require('@aws-cdk/core');
const s3 = require('@aws-cdk/aws-s3');

class CdkworkStack extends cdk.Stack {
  /**
   *
   * @param {cdk.Construct} scope
   * @param {string} id
   * @param {cdk.StackProps=} props
   */
  constructor(scope, id, props) {
    super(scope, id, props);

    const bucketExampleS3Cdk = new s3.Bucket(this, 'example-s3-cdk');
  }
}

module.exports = { CdkworkStack }

Je peux jouer sur la création du bucket pour y intégrer du versionning et un chiffrement par KMS avec une clef définie :

const bucketExampleS3Cdk = new s3.Bucket(this, 'example-s3-cdk', {
      versionning: yes,
      encryption: BucketEncryption.KMS,
      encryptionKey: myKmsKey
    }
);
    
assert(bucket.encryptionKey === myKmsKey)

III.D Pré-conclusion

Clairement, le code devient vite complexe mais ma vision est peut-être biaisée par le fait que j’ai encore des progrès à faire en JS. Pour ne rien vous cacher, j’ai tenté la création de base de données RDS répliquée en multi-AZ, mais c’est un échec pour l’instant.

Peut-être que j’aurais réussi en Python, va savoir. C’est tellement bien, Python.

Conclusion

Ma conclusion et mon retour, c’est que AWS CDK m’éclate vraiment, c’est d’une ergonomie folle pour builder son infrastructure.

L’infrastructure se build tellement vite, et c’est également granulaire au possible dans l’utilisation des possibilités d’AWS, déjà de par l’utilisation de sous-module par service, l’implémentation typiquement objet qui permet l’utilisation d’héritage, de paramètres…

OMG, c’est tellement précis que ça en devient limite chirurgical. Redoutable.

En revanche, il est difficile de comparer ça à Terraform ou d’autres, sachant que ça ne peut s’utiliser que sur AWS (merci captain obvious). Ça n’a donc aucunement vocation à remplacer quoi que ce soit, vous l’aurez compris. Je pense que ça vise surtout à faciliter la vie des développeurs bossant sur AWS en leur épargnant des milliers de lignes de Cloudformation.

Un petit point noir tout de même : la documentation de l’API est encore à améliorer. En effet, j’ai galéré pas mal pour trouver certaines ressources. Elle manque également d’exemples à mon goût.

En conclusion, marre de Cloudformation ? Essayez AWS Cloud Development Kit.

Des bisous.

tags:aws cloud javascript développement infrastructure