Skip to content

Commit 57bb9c8

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 9204cb0 commit 57bb9c8

File tree

3 files changed

+747
-0
lines changed

3 files changed

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

0 commit comments

Comments
 (0)