Skip to content

Commit 2d91f97

Browse files
msirulllil-cain
authored andcommitted
Create ALB-ECS-WithElasticSearchLogs-AppStack
This is the app stack which contains an ECS Task Definition that attaches to an ECS cluster and a CloudWatch Logs group which streams logs to an ElasticSearch cluster from the network template. This also leverages the new cross-stack functionality
1 parent f380645 commit 2d91f97

3 files changed

Lines changed: 757 additions & 0 deletions

File tree

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
from troposphere import Join, GetAtt, ImportValue
2+
from troposphere import Parameter, Ref, Template
3+
from troposphere.ecs import Service as ECSService
4+
from troposphere.ecs import LoadBalancer as ECSLoadBalancer
5+
from troposphere.ecs import TaskDefinition, ContainerDefinition
6+
from troposphere.ecs import Environment, PortMapping, LogConfiguration
7+
from troposphere.ecs import Volume as ECSVolume
8+
from troposphere.iam import Role, PolicyType
9+
import troposphere.elasticloadbalancingv2 as elb
10+
from troposphere.logs import LogGroup
11+
12+
template = Template()
13+
template.add_version("2010-09-09")
14+
15+
template.add_description(
16+
"ALB-ECS App Template")
17+
18+
ref_region = Ref('AWS::Region')
19+
ref_stack_id = Ref('AWS::StackId')
20+
ref_stack_name = Ref('AWS::StackName')
21+
no_value = Ref("AWS::NoValue")
22+
ref_account = Ref("AWS::AccountId")
23+
24+
# Parameters
25+
26+
docker_image = template.add_parameter(Parameter(
27+
"DockerImage",
28+
Type="String",
29+
Description="Docker image to deploy"
30+
))
31+
32+
priority = template.add_parameter(Parameter(
33+
"Priority",
34+
Type="String",
35+
Description="ALB Listener Rule Priority. Can't conflict with another rule"
36+
))
37+
38+
service_name = template.add_parameter(Parameter(
39+
"ServiceName",
40+
Type="String",
41+
Description="Service Name"
42+
))
43+
44+
number_of_containers = template.add_parameter(Parameter(
45+
"NumberOfContainers",
46+
Type="String",
47+
Description="Optionally specify the number of containers of your "
48+
"service you want to run",
49+
Default="1"
50+
))
51+
52+
health_check_path = template.add_parameter(Parameter(
53+
"HealthCheckPath",
54+
Type="String",
55+
Description="Health Check Path. Don't include the base path of your "
56+
"service name. For example, just write: '/ping"
57+
))
58+
59+
container_port = template.add_parameter(Parameter(
60+
"ContainerPort",
61+
Type="String",
62+
Description="This is the port specified in the Dockerfile"
63+
))
64+
65+
env = template.add_parameter(Parameter(
66+
"Environment",
67+
Type="String",
68+
Description="Deployment Environment"
69+
))
70+
71+
database_endpoint = template.add_parameter(Parameter(
72+
"DatabaseEndpoint",
73+
Type="String",
74+
Description="Customer database endpoint"
75+
))
76+
# Imports
77+
78+
vpc = ImportValue(Join("", ["ECSClusterVPC-", Ref(env)]))
79+
ecs_cluster = ImportValue(Join("", ["ECSClusterID-", Ref(env)]))
80+
http_listener = ImportValue(Join("", ["ALBHttpListener-", Ref(env)]))
81+
app_load_balancer = ImportValue(Join("", ["ECSALB-", Ref(env)]))
82+
global_es_cluster_endpoint = ImportValue(Join("",
83+
["GlobalESClusterEndpoint-",
84+
Ref(env)]))
85+
global_es_cluster_arn = ImportValue(Join("", ["GlobalESClusterARN-",
86+
Ref(env)]))
87+
88+
# Resources
89+
90+
app_log_group = template.add_resource(LogGroup(
91+
"AppLogGroup",
92+
RetentionInDays=7
93+
))
94+
95+
96+
service_ecs_role = template.add_resource(Role(
97+
"ECSServiceRole",
98+
AssumeRolePolicyDocument={
99+
"Version": "2008-10-17",
100+
"Statement": [
101+
{
102+
"Effect": "Allow",
103+
"Principal": {
104+
"Service": [
105+
"ecs.amazonaws.com"
106+
]
107+
},
108+
"Action": [
109+
"sts:AssumeRole"
110+
]
111+
}
112+
]
113+
},
114+
ManagedPolicyArns=["arn:aws:iam::aws:policy/service-role/"
115+
"AmazonEC2ContainerServiceRole"],
116+
Path="/"
117+
))
118+
119+
ecs_task_role = template.add_resource(Role(
120+
"ECSTaskRole",
121+
AssumeRolePolicyDocument={
122+
"Version": "2008-10-17",
123+
"Statement": [
124+
{
125+
"Effect": "Allow",
126+
"Principal": {
127+
"Service": [
128+
"ecs-tasks.amazonaws.com"
129+
]
130+
},
131+
"Action": [
132+
"sts:AssumeRole"
133+
]
134+
}
135+
]
136+
},
137+
Path="/"
138+
))
139+
140+
basic_app_permissions = template.add_resource(PolicyType(
141+
"BasicAppPermissions",
142+
PolicyName="BasicAppPermissions",
143+
PolicyDocument={
144+
"Version": "2012-10-17",
145+
"Statement": [
146+
{
147+
"Effect": "Allow",
148+
"Action": [
149+
"logs:Create*",
150+
"logs:PutLogEvents"
151+
],
152+
"Resource": [
153+
Join("", [
154+
"arn:aws:logs:", ref_region, ":",
155+
ref_account, ":log-group:",
156+
Ref(app_log_group), ":*"]),
157+
"*"
158+
]
159+
},
160+
{
161+
"Effect": "Allow",
162+
"Action": [
163+
"cloudwatch:PutMetricData"
164+
],
165+
"Resource": [
166+
"*"
167+
]
168+
}
169+
]
170+
},
171+
Roles=[Ref(ecs_task_role)]
172+
))
173+
174+
docker_containers = [ContainerDefinition(
175+
Cpu=512,
176+
Essential=True,
177+
Image=Ref(docker_image),
178+
Memory=512,
179+
Name=Join("", [Ref(service_name), "-api"]),
180+
Environment=[
181+
Environment(
182+
Name="DB_ENDPOINT",
183+
Value=Ref(database_endpoint)
184+
),
185+
Environment(
186+
Name="CW_LOGS_GROUP",
187+
Value=Ref(app_log_group)
188+
),
189+
Environment(
190+
Name="REGION",
191+
Value=ref_region
192+
)
193+
],
194+
PortMappings=[
195+
PortMapping(
196+
ContainerPort=Ref(container_port)
197+
)
198+
],
199+
LogConfiguration=LogConfiguration(
200+
LogDriver="awslogs",
201+
Options={
202+
"awslogs-group": Ref(app_log_group),
203+
"awslogs-region": ref_region
204+
}
205+
)
206+
)]
207+
208+
service_task = template.add_resource(TaskDefinition(
209+
"ServiceTask",
210+
ContainerDefinitions=docker_containers,
211+
Volumes=[
212+
ECSVolume(
213+
Name="default"
214+
)
215+
],
216+
TaskRoleArn=GetAtt(ecs_task_role, "Arn")
217+
))
218+
219+
target_group_api = template.add_resource(elb.TargetGroup(
220+
"APITargetGroup",
221+
HealthCheckIntervalSeconds="10",
222+
HealthCheckProtocol="HTTP",
223+
HealthCheckTimeoutSeconds="9",
224+
HealthyThresholdCount="2",
225+
HealthCheckPath=Join("", ["/api/", Ref(service_name),
226+
Ref(health_check_path)]),
227+
Matcher=elb.Matcher(
228+
HttpCode="200"),
229+
Name=Join("", [Ref(service_name), "-TargetGroup"]),
230+
Port=Ref(container_port),
231+
Protocol="HTTP",
232+
UnhealthyThresholdCount="2",
233+
VpcId=vpc
234+
))
235+
236+
ecs_service = template.add_resource(ECSService(
237+
"ECSService",
238+
Cluster=ecs_cluster,
239+
DesiredCount=Ref(number_of_containers),
240+
LoadBalancers=[(ECSLoadBalancer(
241+
ContainerName=Join("", [Ref(service_name), "-api"]),
242+
ContainerPort=Ref(container_port),
243+
TargetGroupArn=Ref(target_group_api)
244+
))],
245+
TaskDefinition=Ref(service_task),
246+
Role=Ref(service_ecs_role)
247+
))
248+
249+
template.add_resource(elb.ListenerRule(
250+
"ListenerRuleApi",
251+
ListenerArn=http_listener,
252+
Conditions=[elb.Condition(
253+
Field="path-pattern",
254+
Values=[Join("", ["/api/", Ref(service_name), "/*"])]
255+
)],
256+
Actions=[elb.Action(
257+
Type="forward",
258+
TargetGroupArn=Ref(target_group_api)
259+
)],
260+
Priority=Ref(priority)
261+
))
262+
263+
cw_lambda_role = template.add_resource(Role(
264+
"CWLogsRole",
265+
AssumeRolePolicyDocument={
266+
"Version": "2012-10-17",
267+
"Statement": [
268+
{
269+
"Effect": "Allow",
270+
"Principal": {
271+
"Service": [
272+
"lambda.amazonaws.com"
273+
]
274+
},
275+
"Action": [
276+
"sts:AssumeRole"
277+
]
278+
}
279+
]
280+
},
281+
Path="/"
282+
))
283+
284+
es_pemissions = template.add_resource(PolicyType(
285+
"ESPostingPermissions",
286+
PolicyName="ESPostingPermissions",
287+
PolicyDocument={
288+
"Version": "2012-10-17",
289+
"Statement": [
290+
{
291+
"Effect": "Allow",
292+
"Action": ["es:ESHttpPost", "es:ESHttpPut"],
293+
"Resource": [global_es_cluster_arn,
294+
Join("", [global_es_cluster_arn, "/*"])]
295+
}
296+
]
297+
},
298+
Roles=[Ref(cw_lambda_role)]
299+
))
300+
301+
print(template.to_json())
File renamed without changes.

0 commit comments

Comments
 (0)