Skip to content

Commit 5ea0781

Browse files
committed
initial commit with simple lambda function
1 parent 4dabdeb commit 5ea0781

File tree

12 files changed

+401
-0
lines changed

12 files changed

+401
-0
lines changed

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*.swp
2+
package-lock.json
3+
__pycache__
4+
.pytest_cache
5+
.env
6+
.venv
7+
*.egg-info
8+
9+
# CDK asset staging directory
10+
.cdk.staging
11+
cdk.out

README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
2+
# AWS CDK Python Dev Guide
3+
4+
This project is a template that's intended to serve as a guide for working with CDK in Python.
5+
6+
This project is based on the [aws-cdk-js-dev-guide](https://github.com/therightstuff/aws-cdk-js-dev-guide).
7+
8+
## Tooling setup for local AWS development
9+
10+
### Preamble
11+
12+
It is valuable and necessary to go through the following steps to familiarize yourself with the tools.
13+
14+
- create programmatic user in IAM with admin permissions
15+
- if you're using visual studio code (recommended), [configure aws toolkit](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/setup-toolkit.html)
16+
- set up credentials with the profile id "default"
17+
- get 12 digit account id from My Account in console
18+
- follow [the CDK hello world tutorial](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#hello_world_tutorial)
19+
20+
### Tool Versions
21+
22+
CDK, like SAM, tends to be updated frequently with breaking changes. Prior to committing changes, please ensure that you are using the latest versions and that everything is building and running correctly.
23+
24+
### CDK Initialization
25+
26+
The first step to creating a CDK project is initializing it with `cdk init app` (eg. `cdk init app --language python`), and a CDK project cannot be initialized if the project directory isn't empty. If you would like to use an existing project (like this one) as a template, bear in mind that you will have to rename the stack in multiple locations and it would probably be safer and easier to create a new project and copy and paste in the bits you need (estimated time: 20-30 minutes if you're not familiar with the project structure).
27+
28+
## Python setup
29+
30+
This project is set up like a standard Python project. The initialization
31+
process also creates a virtualenv within this project, stored under the `.venv`
32+
directory. To create the virtualenv it assumes that there is a `python3`
33+
(or `python` for Windows) executable in your path with access to the `venv`
34+
package. If for any reason the automatic creation of the virtualenv fails,
35+
you can create the virtualenv manually.
36+
37+
To manually create a virtualenv on MacOS and Linux:
38+
39+
```bash
40+
python3 -m venv .venv
41+
```
42+
43+
After the init process completes and the virtualenv is created, you can use the following
44+
step to activate your virtualenv.
45+
46+
```bash
47+
source .venv/bin/activate
48+
```
49+
50+
If you are a Windows platform, you would activate the virtualenv like this:
51+
52+
```bash
53+
.venv\Scripts\activate.bat
54+
```
55+
56+
Once the virtualenv is activated, you can install the required dependencies.
57+
58+
```bash
59+
pip install -r requirements.txt
60+
```
61+
62+
At this point you can now synthesize the CloudFormation template for this code.
63+
64+
```bash
65+
cdk synth
66+
```
67+
68+
To add additional dependencies, for example other CDK libraries, just add
69+
them to your `setup.py` file and rerun the `pip install -r requirements.txt`
70+
command.
71+
72+
## Useful commands
73+
74+
- `cdk ls` list all stacks in the app
75+
- `cdk synth` emits the synthesized CloudFormation template(s)
76+
- `cdk deploy` deploy this stack to your default AWS account/region
77+
- `cdk diff` compare deployed stack with current state
78+
79+
### Stack definition
80+
81+
The stack definition is located in the `/aws_cdk_python_dev_guide` folder, this is where the stack is configured for deployment.
82+
83+
See [AWS CDK API documentation](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-construct-library.html) for reference.
84+
85+
#### Lambda Functions
86+
87+
Lambda functions are defined in the `handlers` directory, and include the following samples:
88+
89+
- `simple`: a stateless function
90+
91+
Lambda functions MUST return responses in the following format:
92+
93+
```python
94+
{
95+
"statusCode": 200,
96+
"headers": {},
97+
"body": json.dumps({...})
98+
}
99+
```
100+
101+
#### API Gateway Integrations
102+
103+
When you create a `RestApi` object, the `.root` resource defaults to `/prod/`. You can add HTTP method handlers to the root, or add resource objects and add method handlers to those. To add a resource parameter, simply add a resource enclosed in curly braces (`{}`) and this will be accessible in the `event` object as `event.get("pathParameters")`.
104+
105+
Querystring parameters will be available in the `event` object as `event.get("queryStringParameters")`.
106+
107+
NOTE: it is not possible to rename a path parameter, as cdk will attempt to deploy the new resource before removing the old one and it cannot deploy two resources with the same path structure. The workaround suggested on [the serverless issue thread](https://github.com/serverless/serverless/issues/3785) is to comment out the resource definition, deploy, then uncomment it and deploy again.
108+
109+
`aws_cdk_python_dev_guide/aws_cdk_python_dev_guide_stack.py`:
110+
111+
```python
112+
# Enable CORS for all resources of an api
113+
api = RestApi(self, 'api-name', {
114+
default_cors_preflight_options={
115+
# array containing an origin, or Cors.ALL_ORIGINS
116+
allow_origins: [ cors_origin ],
117+
# array of methods eg. [ 'OPTIONS', 'GET', 'POST', 'PUT', 'DELETE' ]
118+
allow_methods: Cors.ALL_METHODS,
119+
}
120+
})
121+
122+
# OR
123+
124+
# Enable CORS for a specific api resource
125+
api2 = RestApi(self, 'api2-name');
126+
api2_objects = api2.root.addResource('objects');
127+
api2_objects.add_cors_preflight({
128+
# array containing an origin, or Cors.ALL_ORIGINS
129+
allow_origins: [ cors_origin ],
130+
# array of methods eg. [ 'OPTIONS', 'GET', 'POST', 'PUT', 'DELETE' ]
131+
allow_methods: Cors.ALL_METHODS,
132+
})
133+
```
134+
135+
`handlers/myhandler/index.py`:
136+
137+
```python
138+
return {
139+
"statusCode": 200,
140+
"headers": {
141+
'Access-Control-Allow-Origin': os.environ['CORS_ORIGIN'],
142+
'Access-Control-Allow-Credentials': True,
143+
},
144+
"body": json.dumps({ "success": True })
145+
})
146+
```
147+
148+
NOTE: This project defines an origin per stack in the `./stages.json` file, which requires a modification to the `AwsCdkPythonDevGuideStack` signature / kwargs. This is not a CDK requirement, you should configure it in any way that suits your purposes.
149+
150+
For more details see [https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigateway-readme.html) and [https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.CorsOptions.html](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.CorsOptions.html).
151+
152+
### Deployment
153+
154+
By default, CDK deploys stacks that are [environment-agnostic](https://docs.aws.amazon.com/cdk/latest/guide/environments.html). To enable environment-agnostic deployments, run `cdk bootstrap` before `cdk deploy`, but configuring specific regions is probably the safer practice.
155+
156+
To deploy to specific regions, update the `./regions.json` file with the desired region and account numbers.
157+
158+
An example for stack configuration has been provided in `./stages.json`.
159+
160+
To deploy a stack, `cdk deploy <stack name>` (wildcards are supported).
161+
162+
If you don't want to review each set of changes, use the `--require-approval=never` option (not recommended).
163+
164+
The `Outputs` displayed at the end of the process include the API Gateway endpoints. These can be used as-is for the example lambda functions.
165+
166+
### Redeploying a Stack
167+
168+
One of the great advantages of using CDK is that updating a stack is as simple as running the `cdk deploy <stack name>` again.
169+
170+
### Debugging
171+
172+
Testing a lambda function via the API Gateway interface is unlikely to report useful error details. If a function is not behaving correctly or is failing, go to your CloudWatch dashboard and find the log group for the function.
173+
174+
### Deleting a Stack
175+
176+
If for whatever reason you decide you want to delete a stack in its entirety, install the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) and run `aws cloudformation delete-stack --stack-name <stack name> --region <region name>`.

app.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python3
2+
3+
from aws_cdk import core
4+
import json
5+
6+
from aws_cdk_python_dev_guide.aws_cdk_python_dev_guide_stack import AwsCdkPythonDevGuideStack
7+
8+
app = core.App()
9+
10+
with open('stages.json') as stagesJson:
11+
stages = json.load(stagesJson)
12+
13+
with open('regions.json') as regionsJson:
14+
regions = json.load(regionsJson)
15+
16+
for name in stages:
17+
stage = stages.get(name)
18+
for regionKey in stage.get("regions"):
19+
if regionKey:
20+
region = regions.get(regionKey)
21+
AwsCdkPythonDevGuideStack(
22+
app,
23+
f"AwsPythonStack-{name}-{regionKey}",
24+
env=core.Environment(account=region.get("account"), region=region.get("region")),
25+
origin=stage.get("origin"))
26+
else:
27+
# deploy region-agnostic when no region is specified
28+
AwsCdkPythonDevGuideStack(
29+
app,
30+
f"AwsPythonStack-{name}",
31+
origin=stage.get("origin"))
32+
33+
app.synth()

aws_cdk_python_dev_guide/__init__.py

Whitespace-only changes.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from aws_cdk import core
2+
from aws_cdk.aws_apigateway import Cors, RestApi, LambdaIntegration
3+
from aws_cdk.aws_lambda import Function, Runtime, Code
4+
5+
class AwsCdkPythonDevGuideStack(core.Stack):
6+
7+
def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None:
8+
# remove custom keys before calling super
9+
super_kwargs = {k: v for k, v in kwargs.items() if k not in ["origin"]}
10+
super().__init__(scope, construct_id, **super_kwargs)
11+
12+
# set default CORS origin to ALL_ORIGINS
13+
cors_origin = kwargs.get("origin") or "*";
14+
cors_environment = {
15+
"CORS_ORIGIN": cors_origin
16+
}
17+
18+
# reusable RESTful API CORS options object
19+
cors_options = {
20+
"allow_origins": [ cors_origin ], # array containing an origin, or Cors.ALL_ORIGINS
21+
"allow_methods": Cors.ALL_METHODS, # array of methods eg. [ 'OPTIONS', 'GET', 'POST', 'PUT', 'DELETE' ]
22+
}
23+
24+
simple_function = Function(self, "simple-function",
25+
runtime=Runtime.PYTHON_3_8,
26+
code=Code.from_asset("handlers/simple"),
27+
handler="index.main",
28+
environment={
29+
**cors_environment,
30+
"GREETING": "Hello World!"})
31+
32+
simple_api = RestApi(self, "simple-api",
33+
rest_api_name="Simple API sample",
34+
description="Simple API sample with no dependencies",
35+
default_cors_preflight_options=cors_options)
36+
37+
simple_api.root.add_method("GET",
38+
LambdaIntegration(simple_function,
39+
request_templates={"application/json": '{ "statusCode": "200" }'}))
40+

cdk.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"app": "python3 app.py",
3+
"context": {
4+
"@aws-cdk/core:enableStackNameDuplicates": "true",
5+
"aws-cdk:enableDiffNoFail": "true",
6+
"@aws-cdk/core:stackRelativeExports": "true",
7+
"@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
8+
"@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
9+
"@aws-cdk/aws-kms:defaultKeyPolicies": true,
10+
"@aws-cdk/aws-s3:grantWriteWithoutAcl": true
11+
}
12+
}

handlers/simple/index.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import json
2+
import os
3+
4+
def main(event, context):
5+
return_object = {
6+
"success": True,
7+
"querystring": event.get("queryStringParameters"),
8+
"environmentVariables": os.environ['GREETING']
9+
}
10+
return {
11+
"statusCode": 200,
12+
"headers": {
13+
'Access-Control-Allow-Origin': os.environ['CORS_ORIGIN'],
14+
'Access-Control-Allow-Credentials': True,
15+
},
16+
"body": json.dumps(return_object)
17+
}

regions.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"eu": {
3+
"region": "eu-west-1",
4+
"account": "XXXXXXXXXXXX"
5+
},
6+
"za": {
7+
"region": "af-south-1",
8+
"account": "XXXXXXXXXXXX"
9+
}
10+
}

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-e .

setup.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import setuptools
2+
3+
4+
with open("README.md") as fp:
5+
long_description = fp.read()
6+
7+
8+
setuptools.setup(
9+
name="aws_cdk_python_dev_guide",
10+
version="0.0.1",
11+
12+
description="A guide for AWS CDK development using Python",
13+
long_description=long_description,
14+
long_description_content_type="text/markdown",
15+
author="therightstuff",
16+
17+
license='MIT',
18+
19+
package_dir={"": "aws_cdk_python_dev_guide"},
20+
packages=setuptools.find_packages(where="aws_cdk_python_dev_guide"),
21+
22+
install_requires=[
23+
"aws-cdk.assets==1.86.0",
24+
"aws-cdk.aws-apigateway==1.86.0",
25+
"aws-cdk.aws-applicationautoscaling==1.86.0",
26+
"aws-cdk.aws-autoscaling-common==1.86.0",
27+
"aws-cdk.aws-certificatemanager==1.86.0",
28+
"aws-cdk.aws-cloudformation==1.86.0",
29+
"aws-cdk.aws-cloudwatch==1.86.0",
30+
"aws-cdk.aws-codeguruprofiler==1.86.0",
31+
"aws-cdk.aws-ec2==1.86.0",
32+
"aws-cdk.aws-ecr==1.86.0",
33+
"aws-cdk.aws-ecr-assets==1.86.0",
34+
"aws-cdk.aws-efs==1.86.0",
35+
"aws-cdk.aws-elasticloadbalancingv2==1.86.0",
36+
"aws-cdk.aws-events==1.86.0",
37+
"aws-cdk.aws-iam==1.86.0",
38+
"aws-cdk.aws-kms==1.86.0",
39+
"aws-cdk.aws-lambda==1.86.0",
40+
"aws-cdk.aws-logs==1.86.0",
41+
"aws-cdk.aws-route53==1.86.0",
42+
"aws-cdk.aws-s3==1.86.0",
43+
"aws-cdk.aws-s3-assets==1.86.0",
44+
"aws-cdk.aws-sns==1.86.0",
45+
"aws-cdk.aws-sqs==1.86.0",
46+
"aws-cdk.aws-ssm==1.86.0",
47+
"aws-cdk.cloud-assembly-schema==1.86.0",
48+
"aws-cdk.core==1.86.0",
49+
"aws-cdk.custom-resources==1.86.0",
50+
"aws-cdk.cx-api==1.86.0",
51+
"aws-cdk.region-info==1.86.0",
52+
],
53+
54+
python_requires=">=3.6",
55+
56+
classifiers=[
57+
"Development Status :: 4 - Beta",
58+
59+
"Intended Audience :: Developers",
60+
61+
"License :: OSI Approved :: MIT License",
62+
63+
"Programming Language :: JavaScript",
64+
"Programming Language :: Python :: 3 :: Only",
65+
"Programming Language :: Python :: 3.6",
66+
"Programming Language :: Python :: 3.7",
67+
"Programming Language :: Python :: 3.8",
68+
69+
"Topic :: Software Development :: Code Generators",
70+
"Topic :: Utilities",
71+
72+
"Typing :: Typed",
73+
],
74+
)

source.bat

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@echo off
2+
3+
rem The sole purpose of this script is to make the command
4+
rem
5+
rem source .venv/bin/activate
6+
rem
7+
rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows.
8+
rem On Windows, this command just runs this batch file (the argument is ignored).
9+
rem
10+
rem Now we don't need to document a Windows command for activating a virtualenv.
11+
12+
echo Executing .venv\Scripts\activate.bat for you
13+
.venv\Scripts\activate.bat

0 commit comments

Comments
 (0)