AWS Development — Chalice Framework — 5 minutes for Lambda and API Gateway

John Gakhokidze
3 min readSep 7, 2020

--

Since first days in my IT career I was struggling between 2 choices to be infrastructure administrator or developer — I like both. Circumstances pushed me to be infrastructure administrator, and for some time development was just my hobby.
In recent years I was discovering more and more tools from AWS, which was bringing my dream to be developer and infrastructure administrator at the same time — tools making Infrastructure as code reality.

One of the tools is AWS Chalice framework

Best definition of Chalice Framework is:

AWS Chalice is a Python Serverless Microframework for AWS and allows you to quickly create and deploy applications that use Amazon API Gateway and AWS Lambda.

Chalice provides developers with the options:

  1. Quick deploy resources using integrated deploy tool
  2. Test code locally

Administrators can integrate and deploy Chalice application using CDK, Cloud Formation, Terraform . See AWS blog for CDK deployment

There is always question: Which tool to use — Chalice, SAM, CDK, Flask?

Chalice is suitable for small quick deployment:

  1. Developer — to quick test applications.
  2. Deployment in small environment

The scenario #2 is why I exactly decided to use Chalice.
Talking to customer about his EC2 instance pricing, I got the requirements which must be met:

  • Windows Server with 2 remote users to login
  • No Inbound open ports in Security group — Y-e-e-h!
  • No VPN — Guess — this is from Step above
  • Outgoing connections are allowed.
  • Time zones PST/EST/CET — USA/Europe
  • 5 min is ok to start server
  • Server needs to be fast, and we need to save money — Y-e-e-h!
  • No server management from customer

Solution:

  1. Deploy Lambda and API Gateway which will start/stop instance when users connect to server — this will save cost, as total server usage is not expected to be more than 12 hours.
  2. Configure customer side with SSM management plugin installed, and script to start server
  3. Stop EC2 instance when no user activity for an hour — script inside windows server

Implementation: Step 1

  • Docker container with Chalice installed — We are always using containers for Development environment. See Docker file
  • Start container

docker run -v ~/chalice:/root/chalice -v /root/.aws:/root/.aws/ -it -p 2022:22 ubuntu:chalice /bin/bash

chalice new-project control-ec2

cd control-ec2

python3 -m pip show boto3|grep Version|sed 's/Version: /boto3==/'>>requirements.txt #this will add the latest boto3 version to requirements for local testing. You do not to do this if you are ok with code

  • Edit app.py file:

from chalice import Chalice
import boto3
app = Chalice(app_name='control-ec2')

@app.authorizer(ttl_seconds=30)
def my_auth(auth_request):
# Validate auth_request.token, and then:
return AuthResponse(routes=['/'], principal_id='arn:aws:::xxxxxxxxxx:root/*')

# Create route to start instance
@app.route('/start/{name}', authorizer=my_auth)
def instance(name):
instances = [name]
ec2 = boto3.client('ec2', region_name='us-west-2')
ec2.start_instances(InstanceIds=instances)
return {'InstanceId started': name}

# Create route to stop instance
@app.route('/stop/{name}', authorizer=my_auth)
def instance_stop(name):
instances = [name]
ec2 = boto3.client('ec2', region_name='us-west-2')
ec2.stop_instances(InstanceIds=instances)
return {'InstanceId stopped': name}

What we are doing here:

  • Creating Authorizer which allows only IAM user to invoke API method
  • 2 functions to start/stop EC2 instances based on instance Id passed

Note: Change region in boto3 clients and principal id to whatever role/username you need
principal_id='arn:aws:::xxxxxxxxxx:root/*')

In less than 5 minutes you have it ready

How to invoke API?

*Method 1:
Python code

import requests
from requests_aws_sign import AWSV4Sign
from boto3 import session

session = session.Session()
credentials = session.get_credentials()
region = session.region_name
service = 'execute-api'

url = "REPLACE_THIS_LINE_WITH_URI_AND_INSTANCE_ID"
auth = AWSV4Sign(credentials, region, service)
response = requests.get(url, auth=auth)
print(response.text)

Note: you need to modify code to pass instance Id, or explicitly specify URLs with instance ID — e.g. 2 files to start and stop instance

aws apigateway test-invoke-method --rest-api-id API_ID --resource-id RESOURCE_ID --http-method GET --path-with-query-string /stop/<instanceId here>

— rest-api-id — returned after chalice deploy or cat .chalice/deployed/prod.json
--resource-id - find in console on particular method , or from
aws apigateway get-resources --rest-api-id API_ID

From solution we implemented only Step 1.
Stay tune how to configure customer side for Steps 2 and 3

Originally published at https://dev.to on September 7, 2020.

--

--