Monday, 30 December 2019
Getting Hands Dirty with AWS CDK in AWS Cloud9
AWS CDK stands for AWS Cloud Development Kit. It allows us to create and provision AWS infrastructure deployments using programming languages. Currently it supports TypeScript, Java, .Net and Python.
It's been a while and I finally got some time to play with AWS CDK.
Go to IAM, create a user called ``cdk-user`` with AWS Management Console access
![image](https://user-images.githubusercontent.com/35857179/68989119-5c423b00-087d-11ea-999c-7d961499852c.png)
Grant AdministratorAccess to cdk-user
![image](https://user-images.githubusercontent.com/35857179/68987209-93572300-0862-11ea-9fea-06e6234edec9.png)
Go to AWS Cloud 9 and create a new environment
> Note: AWS Cloud9 is a cloud-based integrated development environment (IDE) that lets you write, run, and debug your code with just a browser.
![image](https://user-images.githubusercontent.com/35857179/68987225-c994a280-0862-11ea-819b-e6d0e9e246e9.png)
Leave Step 2 as default,
![image](https://user-images.githubusercontent.com/35857179/68987242-f9dc4100-0862-11ea-9595-423c5574e491.png)
Grab a coffee while waiting for AWS Cloud 9 initialization
![image](https://user-images.githubusercontent.com/35857179/68987250-124c5b80-0863-11ea-81b1-3dc55f18bff0.png)
Install aws-cdk
![image](https://user-images.githubusercontent.com/35857179/68987383-5855ef00-0864-11ea-989f-851e66a1a43b.png)
Let's init a sample app provided by AWS
```python
cdk init sample-app --language python
```
Oops..Got the first error
```
`cdk init` cannot be run in a non-empty directory!
```
By default, AWS Cloud9 workspace comes with a README.md. Let's remove it
```
rm README.md
```
If you are not using AWS Cloud9, you may need to activate the virtualenv providing a self-contained, isolated environment to run our Python code without polluting your system Python.
```
source .env/bin/activate
```
Install the required python module from a file called ``requirements.txt``
```
pip install -r requirements.txt
```
The project structure should look like this
![image](https://user-images.githubusercontent.com/35857179/68987441-19746900-0865-11ea-9362-1402efdd7ab0.png)
This sample app will create the following resources
- SQS Queue
- SQS QueuePolicy
- SNS Topic
- SNS Subscription
- S3 Bucket x 4
- IAM User
The entry point is ``app.py``. It creates two stacks, namely ``hello-cdk-1`` and ``hello-cdk-2`` in ``us-east-2`` and ``us-west-2`` respectively.
```python
#!/usr/bin/env python3
from aws_cdk import core
from hello.hello_stack import MyStack
app = core.App()
MyStack(app, "hello-cdk-1", env={'region': 'us-east-2'})
MyStack(app, "hello-cdk-2", env={'region': 'us-west-2'})
app.synth()
```
Import the required packages.
> Note: For more, you can check the latest API doc [here](https://docs.aws.amazon.com/cdk/api/latest/python/)
```python
from aws_cdk import (
aws_iam as iam,
aws_sqs as sqs,
aws_sns as sns,
aws_sns_subscriptions as subs,
core
)
```
Create a SQS Queue
```python
queue = sqs.Queue(
self, "MyFirstQueue",
visibility_timeout=core.Duration.seconds(300),
)
```
Create a SNS Topic
```python
topic = sns.Topic(
self, "MyFirstTopic",
display_name="My First Topic"
)
```
Subscribe the queue to receive any messages published to the topic
```python
topic.add_subscription(subs.SqsSubscription(queue))
```
``HelloConstruct`` is a custom construct that we defined in our app and it creates four buckets in this stack.
```python
hello = HelloConstruct(self, "MyHelloConstruct", num_buckets=4)
```
``hello/hello_stack.py``
```python
from aws_cdk import (
aws_iam as iam,
aws_s3 as s3,
core,
)
class HelloConstruct(core.Construct):
@property
def buckets(self):
return tuple(self._buckets)
def __init__(self, scope: core.Construct, id: str, num_buckets: int) -> None:
super().__init__(scope, id)
self._buckets = []
for i in range(0, num_buckets):
self._buckets.append(s3.Bucket(self, f"Bucket-{i}"))
def grant_read(self, principal: iam.IPrincipal):
for b in self.buckets:
b.grant_read(principal, "*")
```
Create a user and grant the read permission for the user
```python
user = iam.User(self, "MyUser")
hello.grant_read(user)
```
When we run the CDK app, an AWS CloudFormation template for each stack will be generated. It is called **synthesize** in CDK parlance. To synthesize the app, use ``cdk synth`` with the application name.
```python
cdk synth hello-cdk-1
```
A cfn template will be generated
```yaml
Resources:
MyFirstQueueFF09316A:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 300
Metadata:
aws:cdk:path: hello-cdk-1/MyFirstQueue/Resource
MyFirstQueuePolicy596EEC78:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Statement:
- Action: sqs:SendMessage
Condition:
ArnEquals:
aws:SourceArn:
Ref: MyFirstTopic0ED1F8A4
Effect: Allow
Principal:
Service: sns.amazonaws.com
Resource:
Fn::GetAtt:
- MyFirstQueueFF09316A
- Arn
Version: "2012-10-17"
Queues:
- Ref: MyFirstQueueFF09316A
Metadata:
aws:cdk:path: hello-cdk-1/MyFirstQueue/Policy/Resource
MyFirstQueuehellocdk1MyFirstTopicB252874C505090E8:
Type: AWS::SNS::Subscription
Properties:
Protocol: sqs
TopicArn:
Ref: MyFirstTopic0ED1F8A4
Endpoint:
Fn::GetAtt:
- MyFirstQueueFF09316A
- Arn
Metadata:
aws:cdk:path: hello-cdk-1/MyFirstQueue/hellocdk1MyFirstTopicB252874C/Resource
MyFirstTopic0ED1F8A4:
Type: AWS::SNS::Topic
Properties:
DisplayName: My First Topic
Metadata:
aws:cdk:path: hello-cdk-1/MyFirstTopic/Resource
MyHelloConstructBucket0DAEC57E1:
Type: AWS::S3::Bucket
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: hello-cdk-1/MyHelloConstruct/Bucket-0/Resource
MyHelloConstructBucket18D9883BE:
Type: AWS::S3::Bucket
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: hello-cdk-1/MyHelloConstruct/Bucket-1/Resource
MyHelloConstructBucket2C1DA3656:
Type: AWS::S3::Bucket
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: hello-cdk-1/MyHelloConstruct/Bucket-2/Resource
MyHelloConstructBucket398A5DE67:
Type: AWS::S3::Bucket
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
Metadata:
aws:cdk:path: hello-cdk-1/MyHelloConstruct/Bucket-3/Resource
MyUserDC45028B:
Type: AWS::IAM::User
Metadata:
aws:cdk:path: hello-cdk-1/MyUser/Resource
MyUserDefaultPolicy7B897426:
Type: AWS::IAM::Policy
Properties:
PolicyDocument:
Statement:
- Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
Effect: Allow
Resource:
- Fn::GetAtt:
- MyHelloConstructBucket0DAEC57E1
- Arn
- Fn::Join:
- ""
- - Fn::GetAtt:
- MyHelloConstructBucket0DAEC57E1
- Arn
- /*
- Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
Effect: Allow
Resource:
- Fn::GetAtt:
- MyHelloConstructBucket18D9883BE
- Arn
- Fn::Join:
- ""
- - Fn::GetAtt:
- MyHelloConstructBucket18D9883BE
- Arn
- /*
- Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
Effect: Allow
Resource:
- Fn::GetAtt:
- MyHelloConstructBucket2C1DA3656
- Arn
- Fn::Join:
- ""
- - Fn::GetAtt:
- MyHelloConstructBucket2C1DA3656
- Arn
- /*
- Action:
- s3:GetObject*
- s3:GetBucket*
- s3:List*
Effect: Allow
Resource:
- Fn::GetAtt:
- MyHelloConstructBucket398A5DE67
- Arn
- Fn::Join:
- ""
- - Fn::GetAtt:
- MyHelloConstructBucket398A5DE67
- Arn
- /*
Version: "2012-10-17"
PolicyName: MyUserDefaultPolicy7B897426
Users:
- Ref: MyUserDC45028B
Metadata:
aws:cdk:path: hello-cdk-1/MyUser/DefaultPolicy/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Modules: aws-cdk=1.16.3,@aws-cdk/assets=1.16.3,@aws-cdk/aws-cloudwatch=1.16.3,@aws-cdk/aws-ec2=1.16.3,@aws-cdk/aws-events=1.16.3,@aws-cdk/aws-iam=1.16.3,@aws-cdk/aws-kms=1.16.3,@aws-cdk/aws-lambda=1.16.3,@aws-cdk/aws-logs=1.16.3,@aws-cdk/aws-s3=1.16.3,@aws-cdk/aws-s3-assets=1.16.3,@aws-cdk/aws-sns=1.16.3,@aws-cdk/aws-sns-subscriptions=1.16.3,@aws-cdk/aws-sqs=1.16.3,@aws-cdk/aws-ssm=1.16.3,@aws-cdk/core=1.16.3,@aws-cdk/cx-api=1.16.3,@aws-cdk/region-info=1.16.3,jsii-runtime=Python/3.6.8
```
Before the first deployment, we need to bootstrap the stack.
```
cdk bootstrap
```
It will look like this
```
Bootstrapping environment aws://999999999999/us-east-2...
Bootstrapping environment aws://999999999999/us-west-2...
CDKToolkit: creating CloudFormation changeset...
CDKToolkit: creating CloudFormation changeset...
0/2 | 3:37:20 AM | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket
0/2 | 3:37:21 AM | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket Resource creation Initiated
0/2 | 3:37:20 AM | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket
0/2 | 3:37:20 AM | CREATE_IN_PROGRESS | AWS::S3::Bucket | StagingBucket Resource creation Initiated
1/2 | 3:37:42 AM | CREATE_COMPLETE | AWS::S3::Bucket | StagingBucket
2/2 | 3:37:44 AM | CREATE_COMPLETE | AWS::CloudFormation::Stack | CDKToolkit
Environment aws://999999999999/us-west-2 bootstrapped.
1/2 | 3:37:42 AM | CREATE_COMPLETE | AWS::S3::Bucket | StagingBucket
2/2 | 3:37:43 AM | CREATE_COMPLETE | AWS::CloudFormation::Stack | CDKToolkit
Environment aws://999999999999/us-east-2 bootstrapped.
```
Then we can use ``cdk deploy`` to deploy our CDK app
![image](https://user-images.githubusercontent.com/35857179/68987524-c6e77c80-0865-11ea-9baa-a246948acb68.png)
It shows some warnings here. Enter **y** to continue. The output shows the resources have been created.
![image](https://user-images.githubusercontent.com/35857179/68987597-0e6e0880-0866-11ea-9799-245229a1de05.png)
Let's go to CloudFormation Console. We are able to see the stack we just created
![image](https://user-images.githubusercontent.com/35857179/68987661-5e4ccf80-0866-11ea-87be-7d8edb5a76f0.png)
Under the region, the following resources have been provisioned.
1 SQS queue
![image](https://user-images.githubusercontent.com/35857179/68987859-3448dc80-0869-11ea-9ea3-2a9d04eda625.png)
1 SNS Topic
![image](https://user-images.githubusercontent.com/35857179/68987865-4fb3e780-0869-11ea-835b-bfd38741fa6f.png)
1 Subscribion
![image](https://user-images.githubusercontent.com/35857179/68987872-65291180-0869-11ea-8949-fbdd36fa08aa.png)
1 User
![image](https://user-images.githubusercontent.com/35857179/68987880-7bcf6880-0869-11ea-9938-da5f20894e17.png)
4 Buckets
![image](https://user-images.githubusercontent.com/35857179/68987846-0b284c00-0869-11ea-9a01-1c7744788f46.png)
Let's try something different. Remove all the code from the simple app.
```python
from aws_cdk import (
core,
)
class MyStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# TODO
```
By running ``cdk diff``, we can see the differences between the current app and the deployed one.
![image](https://user-images.githubusercontent.com/35857179/68988008-90146500-086b-11ea-9c4e-9bfb636916c5.png)
Run ``cdk deploy`` again to delete resources
![image](https://user-images.githubusercontent.com/35857179/68988038-4f691b80-086c-11ea-8078-1b88aa068206.png)
Add the following to ``requirements.txt``
```
aws-cdk.aws-events
aws-cdk.aws-events-targets
aws-cdk.aws-lambda
aws-cdk.core
```
Install the required packages from ``requirements.txt``
```
pip install -r requirements.txt
```
Create a folder called ``lambda`` and create a file called ``handler.py`` inside this folder
```python
def handler(event, context):
return {
'statusCode': 200,
'headers': {
'Content-Type': 'text/plain'
},
'body': 'Hello World'
}
```
Back to ``hello_stack.py``, let's made a cron job on AWS Lambda with Scheduled Events
First, import the required packages.
> Note: ``lambda`` is a built-in identifier in Python. Normally we use ``lambda_`` instead.
```python
from aws_cdk import (
aws_events as events,
aws_lambda as lambda_,
aws_events_targets as targets,
core,
)
```
Read ``handler.py`` we just created
```python
with open("./lambda/handler.py", encoding="utf-8") as fp:
code_body = fp.read()
```
Define a lambda function
```python
cronFn = lambda_.Function(
self, "cronFn",
code = lambda_.InlineCode(code_body),
handler = "index.handler",
runtime = lambda_.Runtime.PYTHON_3_7,
)
```
Define an event rule. It will run the job every 6 PM.
```python
rule = events.Rule(
self, "cronRule",
schedule = events.Schedule.cron(
minute = '0',
hour = '18',
month = '*',
week_day = 'MON-FRI',
year = '*'
),
)
```
Finally, add the target to the lambda function
```python
rule.add_target(targets.LambdaFunction(cronFn))
```
Here's the complete code
```python
from aws_cdk import (
aws_events as events,
aws_lambda as lambda_,
aws_events_targets as targets,
core,
)
class MyStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
with open("./lambda/handler.py", encoding="utf-8") as fp:
code_body = fp.read()
cronFn = lambda_.Function(
self, "cronFn",
code = lambda_.InlineCode(code_body),
handler = "index.handler",
runtime = lambda_.Runtime.PYTHON_3_7,
)
rule = events.Rule(
self, "cronRule",
schedule = events.Schedule.cron(
minute = '0',
hour = '18',
month = '*',
week_day = 'MON-FRI',
year = '*'
),
)
rule.add_target(targets.LambdaFunction(cronFn))
```
Let's run ``cdk diff`` again
![image](https://user-images.githubusercontent.com/35857179/68988346-c3a5be00-0870-11ea-90b2-56a1faca9790.png)
After deploying, let's check on the console
![image](https://user-images.githubusercontent.com/35857179/68988415-f69c8180-0871-11ea-9902-a6f477092850.png)
Run a simple test
![image](https://user-images.githubusercontent.com/35857179/68988421-0fa53280-0872-11ea-9cd9-69ebd7e09fa2.png)
Go to CloudWatch, we can see the event we just created
![image](https://user-images.githubusercontent.com/35857179/68988432-48450c00-0872-11ea-9fa3-9a080ecb46fe.png)
To clean up the code, we can simply delete the stack by running ``cdk destroy``.
Subscribe to:
Post Comments (Atom)
A Fun Problem - Math
# Problem Statement JATC's math teacher always gives the class some interesting math problems so that they don't get bored. Today t...
-
SHA stands for Secure Hashing Algorithm and 2 is just a version number. SHA-2 revises the construction and the big-length of the signature f...
-
Contest Link: [https://www.e-olymp.com/en/contests/19775](https://www.e-olymp.com/en/contests/19775) Full Solution: [https://github.com/...
No comments:
Post a Comment