Skip to content

Commit b778e51

Browse files
committed
Serviceable Instance Stack
1 parent c2e1abd commit b778e51

12 files changed

Lines changed: 536 additions & 1 deletion

File tree

.gitallowed

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

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ notes.txt
22

33
.idea/
44

5+
venv/
6+
57
*.swp
68
package-lock.json
79
__pycache__
@@ -11,6 +13,7 @@ __pycache__
1113
*.egg-info
1214

1315
# CDK asset staging directory
16+
cdk.context.json
1417
.cdk.staging
1518
cdk.out
1619
=======

README.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,61 @@
1+
# Welcome to your CDK Python project!
2+
3+
This is a blank project for Python development with CDK.
4+
5+
The `cdk.json` file tells the CDK Toolkit how to execute your app.
6+
7+
This project is set up like a standard Python project. The initialization
8+
process also creates a virtualenv within this project, stored under the `.venv`
9+
directory. To create the virtualenv it assumes that there is a `python3`
10+
(or `python` for Windows) executable in your path with access to the `venv`
11+
package. If for any reason the automatic creation of the virtualenv fails,
12+
you can create the virtualenv manually.
13+
14+
To manually create a virtualenv on MacOS and Linux:
15+
16+
```
17+
$ python -m venv .venv
18+
```
19+
20+
After the init process completes and the virtualenv is created, you can use the following
21+
step to activate your virtualenv.
22+
23+
```
24+
$ source .venv/bin/activate
25+
```
26+
27+
If you are a Windows platform, you would activate the virtualenv like this:
28+
29+
```
30+
% .venv\Scripts\activate.bat
31+
```
32+
33+
Once the virtualenv is activated, you can install the required dependencies.
34+
35+
```
36+
$ pip install -r requirements.txt
37+
```
38+
39+
At this point you can now synthesize the CloudFormation template for this code.
40+
41+
```
42+
$ cdk synth
43+
```
44+
45+
To add additional dependencies, for example other CDK libraries, just add
46+
them to your `setup.py` file and rerun the `pip install -r requirements.txt`
47+
command.
48+
49+
## Useful commands
50+
51+
* `cdk ls` list all stacks in the app
52+
* `cdk synth` emits the synthesized CloudFormation template
53+
* `cdk deploy` deploy this stack to your default AWS account/region
54+
* `cdk diff` compare deployed stack with current state
55+
* `cdk docs` open CDK documentation
56+
57+
Enjoy!
58+
=======
159
# CloudVirtualMachine
2-
A VirtualMachine deployed on the AWS Cloud.
60+
61+
https://github.com/FarrOut/CloudVirtualMachine

app.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env python3
2+
import os
3+
4+
from aws_cdk import core as cdk
5+
6+
# For consistency with TypeScript code, `cdk` is the preferred import name for
7+
# the CDK's core module. The following line also imports it as `core` for use
8+
# with examples from the CDK Developer's Guide, which are in the process of
9+
# being updated to use `cdk`. You may delete this import if you don't need it.
10+
from aws_cdk import core
11+
12+
from cloud_virtual_machine.pipeline_stack import PipelineStack
13+
from cloud_virtual_machine.instance_stack import InstanceStack
14+
from cloud_virtual_machine.logging_stack import LoggingStack
15+
16+
app = core.App()
17+
18+
default_env = core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION'))
19+
rsa_env = core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region='af-south-1')
20+
euro_env = core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region='eu-central-1')
21+
22+
PipelineStack(app, "PipelineStack", env=default_env)
23+
InstanceStack(app, "InstanceStack", env=euro_env)
24+
LoggingStack(app, "LoggingStack", env=euro_env)
25+
26+
# PipelineStack(app, "PipelineStack",
27+
# # If you don't specify 'env', this stack will be environment-agnostic.
28+
# # Account/Region-dependent features and context lookups will not work,
29+
# # but a single synthesized template can be deployed anywhere.
30+
#
31+
# # Uncomment the next line to specialize this stack for the AWS Account
32+
# # and Region that are implied by the current CLI configuration.
33+
#
34+
# env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
35+
# # env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region='af-south-1'),
36+
#
37+
# # Uncomment the next line if you know exactly what Account and Region you
38+
# # want to deploy the stack to. */
39+
#
40+
# #env=core.Environment(account='123456789012', region='us-east-1'),
41+
#
42+
# # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
43+
# )
44+
#
45+
# InstanceStack(app, "InstanceStack",
46+
# # If you don't specify 'env', this stack will be environment-agnostic.
47+
# # Account/Region-dependent features and context lookups will not work,
48+
# # but a single synthesized template can be deployed anywhere.
49+
#
50+
# # Uncomment the next line to specialize this stack for the AWS Account
51+
# # and Region that are implied by the current CLI configuration.
52+
#
53+
# env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')),
54+
# # env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region='af-south-1'),
55+
#
56+
# # Uncomment the next line if you know exactly what Account and Region you
57+
# # want to deploy the stack to. */
58+
#
59+
# #env=core.Environment(account='123456789012', region='us-east-1'),
60+
#
61+
# # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html
62+
# )
63+
#
64+
# LoggingStack(app, "LoggingStack",
65+
# env=core.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region='eu-west-1'),
66+
# )
67+
68+
app.synth()

cdk.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"app": "python app.py",
3+
"context": {
4+
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
5+
"@aws-cdk/core:enableStackNameDuplicates": "true",
6+
"aws-cdk:enableDiffNoFail": "true",
7+
"@aws-cdk/core:stackRelativeExports": "true",
8+
"@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
9+
"@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
10+
"@aws-cdk/aws-kms:defaultKeyPolicies": true,
11+
"@aws-cdk/aws-s3:grantWriteWithoutAcl": true,
12+
"@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true,
13+
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
14+
"@aws-cdk/aws-efs:defaultEncryptionAtRest": true,
15+
"@aws-cdk/aws-lambda:recognizeVersionProps": true,
16+
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
17+
"@aws-cdk/core:newStyleStackSynthesis": true
18+
}
19+
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
from aws_cdk import (core as cdk,
2+
aws_ec2 as ec2,
3+
aws_secretsmanager as secretsmanager,
4+
aws_s3_assets as assets,
5+
aws_autoscaling as autoscaling,
6+
aws_logs as logs,
7+
)
8+
9+
# For consistency with other languages, `cdk` is the preferred import name for
10+
# the CDK's core module. The following line also imports it as `core` for use
11+
# with examples from the CDK Developer's Guide, which are in the process of
12+
# being updated to use `cdk`. You may delete this import if you don't need it.
13+
from aws_cdk import core
14+
# from pipeline_stage import WorkshopPipelineStage
15+
import os
16+
17+
from aws_cdk.core import Duration
18+
19+
20+
class InstanceStack(cdk.Stack):
21+
22+
def __init__(self, scope: cdk.Construct, construct_id: str, **kwargs) -> None:
23+
super().__init__(scope, construct_id, **kwargs)
24+
25+
debug_mode = True # TODO debugging
26+
27+
# =====================
28+
# NETWORKING
29+
# =====================
30+
vpc = ec2.Vpc(self, "VPC",
31+
max_azs=1,
32+
)
33+
34+
# =====================
35+
# SECURITY
36+
# =====================
37+
key_name = 'masterkey-frankfurt'
38+
outer_perimeter_security_group = ec2.SecurityGroup(self, "SecurityGroup",
39+
vpc=vpc,
40+
description="Allow ssh access to ec2 instances",
41+
allow_all_outbound=True
42+
)
43+
outer_perimeter_security_group.add_ingress_rule(ec2.Peer.any_ipv4(), ec2.Port.tcp(22),
44+
"allow ssh access from the world")
45+
46+
# =====================
47+
# STORAGE
48+
# =====================
49+
50+
# =====================
51+
# COMPUTING
52+
# =====================
53+
54+
# CentOS
55+
# centos_bootstrapping = ec2.UserData.for_linux()
56+
# centos_bootstrapping.add_commands()
57+
# amalin_image = ec2.MachineImage.latest_amazon_linux(user_data=centos_bootstrapping)
58+
59+
# Ubuntu
60+
ubuntu_bootstrapping = ec2.UserData.for_linux()
61+
62+
# # https://aws.amazon.com/premiumsupport/knowledge-center/install-cloudformation-scripts/
63+
# # https://gist.github.com/mmasko/66d34b651642525c63cd39251e0c2a8b#gistcomment-3931793
64+
ubuntu_bootstrapping.add_commands(
65+
'sudo apt-get -y update',
66+
'sudo apt-get -y upgrade',
67+
'sudo apt-get -y install python3 python3-pip unzip',
68+
69+
# Download Cloudformation Helper Scripts
70+
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html
71+
'wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.zip',
72+
'python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.zip',
73+
)
74+
75+
# Look up the most recent image matching a set of AMI filters.
76+
# In this case, look up the Ubuntu instance AMI
77+
# in the 'name' field:
78+
ubuntu_image = ec2.LookupMachineImage(
79+
# Canonical, Ubuntu, 20.04 LTS, amd64 focal image build on 2021-04-30
80+
# ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20210430
81+
name="ubuntu/images/*ubuntu-focal-20.04-*-20210430",
82+
owners=["099720109477"],
83+
filters={'architecture': ['x86_64']},
84+
user_data=ubuntu_bootstrapping,
85+
)
86+
87+
image = ubuntu_image
88+
89+
cdk.CfnOutput(self, 'MachineImageOutput',
90+
value=str(image.get_image(self).image_id),
91+
description='MachineImageId',
92+
)
93+
cdk.CfnOutput(self, 'MachineImageUserDataOutput',
94+
value=str(image.get_image(self).user_data.render()),
95+
description='MachineImage UserData',
96+
)
97+
98+
# View instance Logs in the Console
99+
# https: // docs.aws.amazon.com / systems - manager / latest / userguide / monitoring - cloudwatch - agent.html
100+
# https: // docs.aws.amazon.com / AmazonCloudWatch / latest / monitoring / Install - CloudWatch - Agent - New - Instances - CloudFormation.html
101+
# https://aws.amazon.com/blogs/devops/view-cloudformation-logs-in-the-console/
102+
# https://s3.amazonaws.com/cloudformation-templates-us-east-1/CloudWatch_Logs.template
103+
instance_log_group = logs.LogGroup.from_log_group_name(self, 'InstanceLogGroup',
104+
log_group_name='InstanceLogGroup')
105+
106+
working_dir = '/home/ubuntu/cfn-init/'
107+
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html
108+
init_ubuntu = ec2.CloudFormationInit.from_config_sets(
109+
config_sets={
110+
# Applies the configs below in this order
111+
"logging": ['install_cw_agent'],
112+
"testing": ['proof-of-life'],
113+
'connectivity': ['mosh'],
114+
},
115+
configs={
116+
'': ec2.InitConfig([
117+
ec2.InitPackage.apt(
118+
package_name='mosh',
119+
),
120+
# TODO Allow MOSH traffic
121+
# https://linuxhandbook.com/mosh/
122+
# https://mosh.org/#getting
123+
]),
124+
'proof-of-life': ec2.InitConfig([
125+
ec2.InitFile.from_string("~/ifyouseethisitsworking",
126+
"This got written during instance startup"),
127+
]),
128+
'install_cw_agent': ec2.InitConfig([
129+
130+
# Manually create or edit the CloudWatch agent configuration file
131+
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html
132+
133+
# Installing and running the CloudWatch agent on your servers
134+
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html
135+
136+
ec2.InitFile.from_url(
137+
file_name=working_dir + '/amazon-cloudwatch-agent.deb',
138+
url='https://s3.amazonaws.com/amazoncloudwatch-agent/debian/amd64/latest/amazon-cloudwatch'
139+
'-agent.deb'),
140+
ec2.InitCommand.shell_command(
141+
shell_command='dpkg -i -E ./amazon-cloudwatch-agent.deb',
142+
cwd=working_dir)
143+
144+
# TODO Design config file and start CloudWatch agent service
145+
# Installing the CloudWatch agent on new instances using AWS CloudFormation
146+
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Install-CloudWatch-Agent-New-Instances-CloudFormation.html
147+
])
148+
}
149+
)
150+
151+
init = init_ubuntu
152+
instance = ec2.Instance(self, "Instance",
153+
user_data_causes_replacement=True,
154+
vpc=vpc,
155+
instance_type=ec2.InstanceType.of(
156+
ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.SMALL),
157+
machine_image=image,
158+
key_name=key_name,
159+
security_group=outer_perimeter_security_group,
160+
init=init,
161+
162+
# https://docs.aws.amazon.com/cdk/api/latest/python/aws_cdk.aws_ec2/ApplyCloudFormationInitOptions.html
163+
init_options=ec2.ApplyCloudFormationInitOptions(
164+
# Optional, which configsets to activate (['default'] by default)
165+
config_sets=["connectivity"],
166+
167+
# Don’t fail the instance creation when cfn-init fails. You can use this to
168+
# prevent CloudFormation from rolling back when instances fail to start up,
169+
# to help in debugging. Default: false
170+
ignore_failures=debug_mode,
171+
172+
# Optional, how long the installation is expected to take (5 minutes by default)
173+
timeout=Duration.minutes(5),
174+
175+
# Optional, whether to include the --url argument when running cfn-init and cfn-signal commands (false by default)
176+
include_url=False,
177+
178+
# Optional, whether to include the --role argument when running cfn-init and cfn-signal commands (false by default)
179+
include_role=False
180+
),
181+
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC),
182+
)
183+
184+
# asg = autoscaling.AutoScalingGroup(self, "ASG",
185+
# vpc=vpc,
186+
# instance_type=ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3,
187+
# ec2.InstanceSize.MICRO),
188+
# machine_image=image,
189+
# security_group=outer_perimeter_security_group,
190+
# key_name=key_name,
191+
# init=init,
192+
# signals=autoscaling.Signals.wait_for_all(
193+
# timeout=cdk.Duration.minutes(2)
194+
# ),
195+
# update_policy=autoscaling.UpdatePolicy.replacing_update(),
196+
# # Be aware this will reset the size of your AutoScalingGroup on every deployment.
197+
# # See https://github.com/aws/aws-cdk/issues/5215
198+
# desired_capacity=1,
199+
# min_capacity=0,
200+
# max_capacity=5,
201+
# )
202+
203+
# cdk.CfnOutput(self, 'InstanceUserData',
204+
# value=asg.user_data.render(),
205+
# description='UserData for the instances.',
206+
# )
207+
208+
cdk.CfnOutput(self, 'InstancePublicDNSname',
209+
value=instance.instance_public_dns_name,
210+
description='Publicly-routable DNS name for this instance.',
211+
)
212+
213+
user = 'ubuntu'
214+
ssh_command = 'ssh' + ' -i ' + key_name + '.pem ' + user + '@' + instance.instance_public_dns_name
215+
cdk.CfnOutput(self, 'InstanceSSHcommand',
216+
value=ssh_command,
217+
description='Command to SSH into instance.',
218+
)

0 commit comments

Comments
 (0)