AWS

AWS CDK Basics

brightlightkim 2022. 6. 17. 05:47

Initalize the project

Before writing any code for our CloudFormation to AWS CDK migration, we should initialise our AWS CDK project to get started.

In a command-line shell, create a directory to contain the project, and go to that directory:

mkdir my-cfn-cdk-migration

cd my-cfn-cdk-migration
 

In this directory, we will use the CDK command-line tool to initialise a project for Typescript. We use the init app sub-command with the —language option set to either typescript, python, or go.

❯ cdk init app --language=typescript
 

Applying project template app for typescript

❯ cdk init app --language=python

Applying project template app for python

Each initialisation will create slightly different files, depending on the language. But before looking at any of these files, let us save the CloudFormation template we choose to use in our project file structure. We can create a directory named cfn and store the template in there as sample.template. If you use Linux, macOS, or Linux on Windows, you could run the command below to use the sample template mentioned:

❯ mkdir cfn

❯ curl --output cfn/sample.template <https://s3.eu-west-1.amazonaws.com/cloudformation-templates-eu-west-1/EC2InstanceWithSecurityGroupSample.template>
 

For each of the languages chosen, we will initially focus on just a single file to write the AWS CDK code to use our existing CloudFormation template (cfn/sample.template):

  • Typescript: bin/my-cfn-cdk-migration.ts
  • Python: app.py

We will just start with the code, and then explain the details step by step, along with testing how that works. You see both Typescript and Python code below, and you will find them reasonably similar. The code comprises essentially five areas:

  • Import of modules and class/structure definitions
  • Environment setup
  • AWS CDK App and Stack initialisation
  • Inclusion of CloudFormation template
  • Synthesize CloudFormation
#!/usr/bin/env node
import 'source-map-support/register';
import * as path from 'path';
import { App, Environment, Stack } from 'aws-cdk-lib';
import { CfnInclude } from 'aws-cdk-lib/cloudformation-include';

const env: Environment = {
  account: process.env.CDK_DEFAULT_ACCOUNT,
  region: process.env.CDK_DEFAULT_REGION,
};

const stackName = 'mystack';

const app = new App();
const stack = new Stack(app, 'wrapper-stack', { env, stackName });

new CfnInclude(stack, 'included-template', {
  templateFile: path.join(process.cwd(), 'cfn', 'sample.template'),
});

app.synth();
#!/usr/bin/env python3
import os

from aws_cdk import (
    App,
    Environment,
    Stack
)
from aws_cdk.cloudformation_include import CfnInclude

env = Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION'))

stack_name = 'mystack'
app = App()
stack = Stack(app, 'wrapper-stack', env=env, stack_name=stack_name)
CfnInclude(stack, 'included-template', template_file=os.path.join(os.getcwd(), 'cfn', 'sample.template'))
app.synth()
 

Let us start with the App and Stack parts. When we use the AWS CDK, all infrastructure we manage as some kind of larger scale unit is grouped into an App. This the top-level container, which contains everything that you can provision in the project or solution you are working with.

 

Each App may comprise one or more stacks. A Stack is a collection of AWS resources that you provide and manage as a unit. It could be your whole solution, or it could be just one part of a solution. An AWS CDK stack maps 1-to-1 with a CloudFormation stack.

 

For Stacks, we can in AWS CDK attach Constructs, which are a logical grouping of Resources, which are the same type of resources that you represent in CloudFormation as well. In the code, we have a CfnInclude construct, which performs the inclusion of a CloudFormation template, a Stack and an App in AWS CDK. The only parameter here so far is to point to the CloudFormation template in our project. This is the stuff to include into the AWS CDK App and the related stack.

 

We also have the concept of an Environment, which in AWS CDK context is a combination of AWS account and AWS region. A CDK App can target multiple accounts and/or multiple regions within the same app.

The environment setup in the code is essentially using two environment variables that the AWS CDK has built in, which captures whatever is the current AWS account id and AWS region, based on the currently active AWS credentials that you use. CDK_DEFAULT_ACCOUNT and CDK_DEFAULT_REGION are the names of these environment variables.

 

So this approach assumes you have done your work to activate an AWS credentials profile, or set environment variables for credentials or equivalent, and then the CDK code will get the account and region info from that. This can be fine for development environments, but in particular with production environments, you would want some additional guardrails. For now, that is not our focus, though.

 

The final area is to synthesize to CloudFormation. This the task of the code app.synth(). It generates the underlying CloudFormation.

 

You can put the code into the program files mentioned above, app.py or bin/my-cfn-cdk-migration.ts.

 

You can see the hierarchy shown in the picture in the code itself. The App itself is just created, we have an object for that. The line with the Stack creation adds a reference to the App, plus a name which is the name of the stack in this case. After that, there are additional parameters for the environment.

 

The CfnInclude creation follows the same pattern. It adds a reference to the Stack, plus a name. After that, there are additional parameters, in this case, a reference to the CloudFormation template file.

 

All logical resource groupings in AWS CDK follow the same pattern - a reference to the parent in a hierarchy and a name. With a Stack, the name is the same as the name of the CloudFormation stack in AWS. Below the stack level, into construct territory, there is less concern with having a specific name. However, a name will need to be unique under a specific parent in the hierarchy.

 

Note: If you have not already deployed sample.template CloudFormation to an environment without using the AWS CDK, now is the time to do this. We will assume below that the CloudFormation template already has been deployed, and the work we do will be in the light of changes to do when we have an existing (non-CDK) infrastructure already in place.

 

So deploy it, if you need to. We will wait…

… alright, done now? Great!

Also, note down the name of the CloudFormation stack you have deployed. This is important. If the name is not the same as the name set in the Stack object in the code, change the name of the stack in the code to match the deployed stack.

 

The AWS CDK initialisation we did before with the cdk init command behaved slightly differently depending on the language we choose to use. For Typescript, the cdk init performed the needed package installation. For Python, we have a bit more to do:

For Python, activate the virtual environment that cdk init created for you. In Linux och macOS, run

source ./.venv/bin/activate

and in Windows

.venv\Scripts\activate.bat

To activate the virtual environment that has been set up. Then run the package installation:

pip3 install -r requirements.txt

This will install the required AWS CDK packages. We have now:

  • An initialised AWS CDK project
  • Required AWS CDK packages installed
  • An AWS CDK app wrapper around our existing CloudFormation template

Now it is time to look closer to the code and actually check that this AWS CDK app actually works!

Checking for CloudFormation changes

Since the plan here is to do a migration from an existing CloudFormation infrastructure deployment to use AWS CDK, we want really everything to be the same when we use AWS CDK. No messing around with our existing infrastructure!

So what we want first is to check that what we have with AWS CDK matches what we have already deployed before. We can use the AWS CDK command-line tool for this. All commands should be executed in the same directory as the cdk.json file is in, which is the root directory of our project.

 

Note: you have to have your AWS credentials in place when you run the AWS CDK commands, since you will work towards the target AWS environment. It does not matter whether you set AWS regular environment variables (e.g. AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, etc) or use AWS credentials profiles. If you use AWS credentials profiles, add a –profile profile_name to each command line to specify the name of the profile, if the profile name is not default.

 

The AWS CDK command-line tool has a sub-command diff, which can compare the under-the-hood generated CloudFormation with what is actually deployed. If you run the cdk diff command and run it with the sample template we have, and the stack name is correct, and you have set the AWS credentials for your environment, see the following output (expect probably to wait 10-15 seconds):

❯ cdk diff       
[Info at /mystack/included-template/$Mappings/AWSInstanceType2Arch] Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap
[Info at /mystack/included-template/$Mappings/AWSInstanceType2NATArch] Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap
[Info at /mystack/included-template/$Mappings/AWSRegionArch2AMI] Consider making this CfnMapping a lazy mapping by providing `lazy: true`: either no findInMap was called or every findInMap could be immediately resolved without using Fn::FindInMap
Stack mystack
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}
 

If you use your own CloudFormation template and stack, the FindInMap warnings will probably not be the same, since these depend on the specific CloudFormation template in use. We can ignore these warnings for now as well. It is only a potential optimisation that could be done.

 

There are two resources that are shown with plus signs, so if we deploy the stack in the AWS CDK App, these would be added to the stack. There are no resources with minus signs in front of them, which means there are no resources removed. The differences are shown at the CloudFormation level, so it is beneficial to know a bit of CloudFormation for this type of operation.

 

What are these added entries?

 

The first entry is an added CloudFormation template parameter, called BootstrapVersion. The value will automatically be retrieved from this parameter in Parameter Store when CloudFormation deploys the stack.

 

The second entry is a CloudFormation rule, which performs validation of BootstrapVersion. Have a recent enough bootstrap done, otherwise the stack deployment will fail.

 

We will cover the bootstrapping in the next section below. What we can see here, though, is that, except for this bootstrapping part, there are no changes. As long as we can accept this bootstrap parameter and added rule validation, it will be all the same.

 

All AWS CDK stacks will have this type of parameter and rule. It is part of the baseline you can expect.

You can see the generated CloudFormation template from AWS CDK by running the cdk synth command. The output should be identical to the original CloudFormation template, with the addition of the parameter and rule for BootstrapVersion.

'AWS' 카테고리의 다른 글

AWS Down 9/28/2022  (0) 2022.09.29
AWS CloudFormation vs AWS Cloud Development Kit(CDK)  (0) 2022.06.17
Benefits of the AWS Cloud  (0) 2022.05.07
AWS Well-Architected Framework  (0) 2022.05.07
[AWS S3] Securing AWS S3 uploads using presigned URLs  (0) 2022.05.07