Elastic Beanstalk
Introduction
The manual setup steps taken from the AWS docs on Getting started using Elastic Beanstalk.
Have been automated using the CDK.
Examples follows in Python.
3 Stacks were created.
-
BeanstalkS3Stack [ uploads assets to S3 ]
-
BeanstalkAppStack [ creates, deploy new app ]
-
BeanstalkEnvStack [ creates new environment ]
By default the entry point app.py
is created during the init stage of creating the CDK project.
This is where you define which stacks to use.
When you run cdk ls
it will then list the stacks you have defined to use in the app.py
In this case it will return the 3 stacks listed above.
App.py
#!/usr/bin/env python3
from aws_cdk import core
from elastic_beanstalk.BeanstalkAppStack import BeanstalkAppStack
from elastic_beanstalk.BeanstalkEnvStack import BeanstalkEnvStack
from elastic_beanstalk.BeanstalkS3Stack import BeanstalkS3Stack
app = core.App()
# a dictionary to store properties
props = {
'namespace': 'ElasticBeanstalk',
'application_name':'GettingStartedApp2',
'environment_name': 'GettingStartedEnv2',
'solution_stack_name': '64bit Amazon Linux 2018.03 v2.15.5 running Go 1.14.4',
's3_asset' : 'assets/go-v1.zip'
}
s3_bucket = BeanstalkS3Stack(
app,
f"{props['namespace']}-s3",
props
)
beanstalk_app = BeanstalkAppStack(
app,
f"{props['namespace']}-app",
s3_bucket.outputs
)
# the beanstalk app stack has a dependency on the creation of a S3 bucket
beanstalk_app.add_dependency(s3_bucket)
beanstalk_env = BeanstalkEnvStack(
app,
f"{props['namespace']}-env",
props,
)
# the beanstalk environment stack has a dependency on the creation of a beanstalk app
beanstalk_env.add_dependency(beanstalk_app)
app.synth()
BeanstalkS3Stack
In order to store assets to deploy into a Beanstalk environment, a S3 bucket is leveraged.
Here we use the S3 construct aws_s3
.
The S3 asset used are taken from the go sample app.
from aws_cdk import (
core,
aws_s3,
aws_s3_deployment,
aws_s3_assets,
)
import os
class BeanstalkS3Stack(core.Stack):
def __init__(self, scope: core.Construct, id: str,props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# the asset is uploaded to the cdktoolkit-stagingbucket
s3_bucket_asset = aws_s3_assets.Asset(
self,
"s3-asset",
path=os.path.abspath(props['s3_asset'])
)
# debugging print s3 object url to console output
output = core.CfnOutput(
self,
"S3_object_url",
value=s3_bucket_asset.s3_object_url,
description="S3 object url"
)
output = core.CfnOutput(
self,
"S3_object_key",
value=s3_bucket_asset.s3_object_key,
description="S3 object key"
)
output = core.CfnOutput(
self,
"S3_bucket_name",
value=s3_bucket_asset.s3_bucket_name,
description="S3 bucket name"
)
self.output_props = props.copy()
self.output_props['s3bucket_name'] = s3_bucket_asset.s3_bucket_name
self.output_props['s3bucket_obj_key'] = s3_bucket_asset.s3_object_key
# pass objects to another stack
@property
def outputs(self):
return self.output_props
Create Application
BeanstalkAppStack
Creating a new application involves using the Construct taken from aws_elasticbeanstalk
from aws_cdk import (
core,
aws_elasticbeanstalk as elastic_beanstalk,
aws_s3
)
class BeanstalkAppStack(core.Stack):
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
def createApplication(application_name):
elastic_beanstalk.CfnApplication(
self,
"Elastic-Beanstalk",
application_name=application_name,
description="AWS Elastic Beanstalk Demo",
)
createApplication(props['application_name'])
BeanstalkAppVersionStack(self,"BeanstalkAppVersionStack",props,**kwargs)
class BeanstalkAppVersionStack(core.NestedStack):
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
app_version = elastic_beanstalk.CfnApplicationVersion(
self,
"application_version",
application_name=props['application_name'],
source_bundle=elastic_beanstalk.CfnApplicationVersion.SourceBundleProperty(
s3_bucket=props['s3bucket_name'],
s3_key=props['s3bucket_obj_key']
),
)
BeanstalkAppVersionStack
In the code above, a new Beanstalk Application is created.
BeanstalkAppVersionStack
is defined as a nested stack.
This stack deploys a new application version.
Defining a nested stack allows the support of dependencies.
BeanstalkAppVersionStack
has a dependency on BeanstalkAppStack
You can’t deploy a new version when you don’t have an application.
Create Environment
BeanstalkEnvStack
from aws_cdk import (
core,
aws_elasticbeanstalk as elastic_beanstalk
)
import boto3
class BeanstalkEnvStack(core.Stack):
def createEnvironment(self,application_name,environment_name,solution_stack_name):
# get the latest beanstalk application version
client = boto3.client('elasticbeanstalk')
application_versions = client.describe_application_versions(
ApplicationName=application_name
)
version_label = None
if(len(application_versions['ApplicationVersions'])> 0):
version_label = application_versions['ApplicationVersions'][0]['VersionLabel']
beanstalk_env_config_template = elastic_beanstalk.CfnConfigurationTemplate(
self,
"Elastic-Beanstalk-Env-Config",
application_name=application_name,
solution_stack_name=solution_stack_name,
option_settings=[
elastic_beanstalk.CfnConfigurationTemplate.ConfigurationOptionSettingProperty(
namespace="aws:autoscaling:asg",option_name="MinSize",value="2"
),
elastic_beanstalk.CfnConfigurationTemplate.ConfigurationOptionSettingProperty(
namespace="aws:autoscaling:asg",option_name="MaxSize",value="4"
)
]
)
# configure the environment for auto-scaling
beanstalk_env = elastic_beanstalk.CfnEnvironment(
self,
"Elastic-Beanstalk-Environment",
application_name=application_name,
environment_name=environment_name,
solution_stack_name=solution_stack_name,
version_label=version_label,
option_settings=[
elastic_beanstalk.CfnEnvironment.OptionSettingProperty(
namespace="aws:autoscaling:asg",option_name="MinSize",value="2"
),
elastic_beanstalk.CfnEnvironment.OptionSettingProperty(
namespace="aws:autoscaling:asg",option_name="MaxSize",value="4"
),
]
)
def __init__(self, scope: core.Construct, id: str, props, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
self.createEnvironment(props['application_name'],props['environment_name'],props['solution_stack_name'])
Diff
Run a cdk diff
to see what changes will be applied in AWS.
Synth
If you wish to see what the CloudFormation templates look like.
cdk synth [stack-name]
.
By default these are stored inside the cdk.out
directory.
Deploy
Wildcards are supported when using cdk deploy.
So, cdk deploy Beanstalk*
will work.
Validate
Once deployed, you should be able to reach your new Beanstalk application using the public facing dynamic url.
In this case a welcome web page will open.
Destroy/clean up
To avoid un-necessary AWS costs, destroy the Beanstalk resources.
cdk destroy Beanstalk*