Skip to content

Deploy EC2 instance using the AWS CDK for Python

Setup for macOS

Make sure you do this setup first:

  1. Setup macOS for AWS Cloud DevOps
  2. AWS Authentication
  3. Install AWS CDK on macOS

Create the CDK Project

Open a Terminal and type this to the create project directory:

mkdir -p ~/aws-ec2-cdk-python
cd ~/aws-ec2-cdk-python

Create the project:

nvm use 20.16.0

# init project and create Python virtual environment
cdk init app --language python --generate-only

# activate virtual environment
source .venv/bin/activate

# install Python packages
pip install -r requirements.txt
pip install -r requirements-dev.txt

The result should be this file tree:

tree -L 3
.
├── README.md
├── app
   ├── __init__.py
   └── app_stack.py
├── app.py
├── cdk.json
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
    ├── __init__.py
    └── unit
        ├── __init__.py
        └── test_app_stack.py

Define a single instance stack that runs Nginx

Update stack code

Paste this code in app/app_stack.py:

from aws_cdk import (
    Stack,
    aws_ec2 as ec2,
    aws_iam as iam,
)
from constructs import Construct

class AppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        # Use the default VPC
        vpc = ec2.Vpc.from_lookup(self, "VPC", is_default=True)

        # Create a security group
        security_group = ec2.SecurityGroup(
            self, "NginxSecurityGroup",
            vpc=vpc,
            description="Allow HTTP traffic",
            allow_all_outbound=True
        )
        security_group.add_ingress_rule(
            ec2.Peer.any_ipv4(),
            ec2.Port.tcp(80),
            "Allow HTTP traffic from anywhere"
        )

        # Create a role for the EC2 instance
        role = iam.Role(
            self, "NginxInstanceRole",
            assumed_by=iam.ServicePrincipal("ec2.amazonaws.com")
        )

        # Create the EC2 instance
        instance = ec2.Instance(
            self, "NginxInstance",
            instance_type=ec2.InstanceType("t2.micro"),
            machine_image=ec2.AmazonLinuxImage(generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2),
            vpc=vpc,
            vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC),
            role=role,
            security_group=security_group,
        )

        # Add user data to install and start nginx
        instance.add_user_data(
            "yum update -y",
            "amazon-linux-extras install nginx1 -y",
            "systemctl start nginx",
            "systemctl enable nginx"
        )

        # Output the public IP of the instance
        self.output_props = {
            'instance_public_ip': instance.instance_public_ip
        }

Update app code

Paste this code into app.py:

#!/usr/bin/env python3
import os

import aws_cdk as cdk

from app.app_stack import AppStack


app = cdk.App()

AppStack(
    app, 
    construct_id="NginxEc2Stack",
    env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
)

app.synth()

Deploy

Print the CloudFormation template for the stack. You should see the CloudFormation template without any errors:

cdk synth --profile default
Bootstrap the CDK environment. This should be done only once per account. Skip this step if you have done it already. See Bootstrapping for details:

cdk bootstrap --profile default
Deploy the stack:

cdk deploy --profile default

Test Deployment

Check that you can browse the nginx default site:

key="aws-ec2-key"
instance="NginxEc2Stack/NginxInstance"

instance_public_ip=$(aws ec2 describe-instances \
    --filters \
       Name=tag:Name,Values=$instance \
        Name=instance-state-name,Values=running \
| jq -r '.Reservations[0].Instances[0].PublicIpAddress')

open http://$instance_public_ip

Cleanup

Delete the stack:

cdk destroy --profile default

Opptional: To delete the CDKToolkit CloudFormation template which is created by the AWS CDK during bootstrap:

aws cloudformation delete-stack --stack-name CDKToolkit --profile default
The bootstrap process creates an S3 Bucket with a name: cdk-<hash>-assets-<account_id>-<region>. This bucket will not be deleted automatically and will remain in your account if not deleted manually.

GitHub Repository

The code for this project is available in the aws-ec2-cdk-python repository.