|
18 | 18 | "VpcPublicSubnet1Subnet5C2D37C4": {
|
19 | 19 | "Type": "AWS::EC2::Subnet",
|
20 | 20 | "Properties": {
|
21 |
| - "VpcId": { |
22 |
| - "Ref": "Vpc8378EB38" |
23 |
| - }, |
24 | 21 | "AvailabilityZone": {
|
25 | 22 | "Fn::Select": [
|
26 | 23 | 0,
|
|
44 | 41 | "Key": "Name",
|
45 | 42 | "Value": "integ-default-capacity-provider/Vpc/PublicSubnet1"
|
46 | 43 | }
|
47 |
| - ] |
| 44 | + ], |
| 45 | + "VpcId": { |
| 46 | + "Ref": "Vpc8378EB38" |
| 47 | + } |
48 | 48 | }
|
49 | 49 | },
|
50 | 50 | "VpcPublicSubnet1RouteTable6C95E38E": {
|
51 | 51 | "Type": "AWS::EC2::RouteTable",
|
52 | 52 | "Properties": {
|
53 |
| - "VpcId": { |
54 |
| - "Ref": "Vpc8378EB38" |
55 |
| - }, |
56 | 53 | "Tags": [
|
57 | 54 | {
|
58 | 55 | "Key": "Name",
|
59 | 56 | "Value": "integ-default-capacity-provider/Vpc/PublicSubnet1"
|
60 | 57 | }
|
61 |
| - ] |
| 58 | + ], |
| 59 | + "VpcId": { |
| 60 | + "Ref": "Vpc8378EB38" |
| 61 | + } |
62 | 62 | }
|
63 | 63 | },
|
64 | 64 | "VpcPublicSubnet1RouteTableAssociation97140677": {
|
|
75 | 75 | "VpcPublicSubnet1DefaultRoute3DA9E72A": {
|
76 | 76 | "Type": "AWS::EC2::Route",
|
77 | 77 | "Properties": {
|
78 |
| - "RouteTableId": { |
79 |
| - "Ref": "VpcPublicSubnet1RouteTable6C95E38E" |
80 |
| - }, |
81 | 78 | "DestinationCidrBlock": "0.0.0.0/0",
|
82 | 79 | "GatewayId": {
|
83 | 80 | "Ref": "VpcIGWD7BA715C"
|
| 81 | + }, |
| 82 | + "RouteTableId": { |
| 83 | + "Ref": "VpcPublicSubnet1RouteTable6C95E38E" |
84 | 84 | }
|
85 | 85 | },
|
86 | 86 | "DependsOn": [
|
|
102 | 102 | "VpcPublicSubnet1NATGateway4D7517AA": {
|
103 | 103 | "Type": "AWS::EC2::NatGateway",
|
104 | 104 | "Properties": {
|
105 |
| - "SubnetId": { |
106 |
| - "Ref": "VpcPublicSubnet1Subnet5C2D37C4" |
107 |
| - }, |
108 | 105 | "AllocationId": {
|
109 | 106 | "Fn::GetAtt": [
|
110 | 107 | "VpcPublicSubnet1EIPD7E02669",
|
111 | 108 | "AllocationId"
|
112 | 109 | ]
|
113 | 110 | },
|
| 111 | + "SubnetId": { |
| 112 | + "Ref": "VpcPublicSubnet1Subnet5C2D37C4" |
| 113 | + }, |
114 | 114 | "Tags": [
|
115 | 115 | {
|
116 | 116 | "Key": "Name",
|
|
126 | 126 | "VpcPublicSubnet2Subnet691E08A3": {
|
127 | 127 | "Type": "AWS::EC2::Subnet",
|
128 | 128 | "Properties": {
|
129 |
| - "VpcId": { |
130 |
| - "Ref": "Vpc8378EB38" |
131 |
| - }, |
132 | 129 | "AvailabilityZone": {
|
133 | 130 | "Fn::Select": [
|
134 | 131 | 1,
|
|
152 | 149 | "Key": "Name",
|
153 | 150 | "Value": "integ-default-capacity-provider/Vpc/PublicSubnet2"
|
154 | 151 | }
|
155 |
| - ] |
| 152 | + ], |
| 153 | + "VpcId": { |
| 154 | + "Ref": "Vpc8378EB38" |
| 155 | + } |
156 | 156 | }
|
157 | 157 | },
|
158 | 158 | "VpcPublicSubnet2RouteTable94F7E489": {
|
159 | 159 | "Type": "AWS::EC2::RouteTable",
|
160 | 160 | "Properties": {
|
161 |
| - "VpcId": { |
162 |
| - "Ref": "Vpc8378EB38" |
163 |
| - }, |
164 | 161 | "Tags": [
|
165 | 162 | {
|
166 | 163 | "Key": "Name",
|
167 | 164 | "Value": "integ-default-capacity-provider/Vpc/PublicSubnet2"
|
168 | 165 | }
|
169 |
| - ] |
| 166 | + ], |
| 167 | + "VpcId": { |
| 168 | + "Ref": "Vpc8378EB38" |
| 169 | + } |
170 | 170 | }
|
171 | 171 | },
|
172 | 172 | "VpcPublicSubnet2RouteTableAssociationDD5762D8": {
|
|
183 | 183 | "VpcPublicSubnet2DefaultRoute97F91067": {
|
184 | 184 | "Type": "AWS::EC2::Route",
|
185 | 185 | "Properties": {
|
186 |
| - "RouteTableId": { |
187 |
| - "Ref": "VpcPublicSubnet2RouteTable94F7E489" |
188 |
| - }, |
189 | 186 | "DestinationCidrBlock": "0.0.0.0/0",
|
190 | 187 | "GatewayId": {
|
191 | 188 | "Ref": "VpcIGWD7BA715C"
|
| 189 | + }, |
| 190 | + "RouteTableId": { |
| 191 | + "Ref": "VpcPublicSubnet2RouteTable94F7E489" |
192 | 192 | }
|
193 | 193 | },
|
194 | 194 | "DependsOn": [
|
|
210 | 210 | "VpcPublicSubnet2NATGateway9182C01D": {
|
211 | 211 | "Type": "AWS::EC2::NatGateway",
|
212 | 212 | "Properties": {
|
213 |
| - "SubnetId": { |
214 |
| - "Ref": "VpcPublicSubnet2Subnet691E08A3" |
215 |
| - }, |
216 | 213 | "AllocationId": {
|
217 | 214 | "Fn::GetAtt": [
|
218 | 215 | "VpcPublicSubnet2EIP3C605A87",
|
219 | 216 | "AllocationId"
|
220 | 217 | ]
|
221 | 218 | },
|
| 219 | + "SubnetId": { |
| 220 | + "Ref": "VpcPublicSubnet2Subnet691E08A3" |
| 221 | + }, |
222 | 222 | "Tags": [
|
223 | 223 | {
|
224 | 224 | "Key": "Name",
|
|
234 | 234 | "VpcPrivateSubnet1Subnet536B997A": {
|
235 | 235 | "Type": "AWS::EC2::Subnet",
|
236 | 236 | "Properties": {
|
237 |
| - "VpcId": { |
238 |
| - "Ref": "Vpc8378EB38" |
239 |
| - }, |
240 | 237 | "AvailabilityZone": {
|
241 | 238 | "Fn::Select": [
|
242 | 239 | 0,
|
|
260 | 257 | "Key": "Name",
|
261 | 258 | "Value": "integ-default-capacity-provider/Vpc/PrivateSubnet1"
|
262 | 259 | }
|
263 |
| - ] |
| 260 | + ], |
| 261 | + "VpcId": { |
| 262 | + "Ref": "Vpc8378EB38" |
| 263 | + } |
264 | 264 | }
|
265 | 265 | },
|
266 | 266 | "VpcPrivateSubnet1RouteTableB2C5B500": {
|
267 | 267 | "Type": "AWS::EC2::RouteTable",
|
268 | 268 | "Properties": {
|
269 |
| - "VpcId": { |
270 |
| - "Ref": "Vpc8378EB38" |
271 |
| - }, |
272 | 269 | "Tags": [
|
273 | 270 | {
|
274 | 271 | "Key": "Name",
|
275 | 272 | "Value": "integ-default-capacity-provider/Vpc/PrivateSubnet1"
|
276 | 273 | }
|
277 |
| - ] |
| 274 | + ], |
| 275 | + "VpcId": { |
| 276 | + "Ref": "Vpc8378EB38" |
| 277 | + } |
278 | 278 | }
|
279 | 279 | },
|
280 | 280 | "VpcPrivateSubnet1RouteTableAssociation70C59FA6": {
|
|
291 | 291 | "VpcPrivateSubnet1DefaultRouteBE02A9ED": {
|
292 | 292 | "Type": "AWS::EC2::Route",
|
293 | 293 | "Properties": {
|
294 |
| - "RouteTableId": { |
295 |
| - "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" |
296 |
| - }, |
297 | 294 | "DestinationCidrBlock": "0.0.0.0/0",
|
298 | 295 | "NatGatewayId": {
|
299 | 296 | "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
|
| 297 | + }, |
| 298 | + "RouteTableId": { |
| 299 | + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" |
300 | 300 | }
|
301 | 301 | }
|
302 | 302 | },
|
303 | 303 | "VpcPrivateSubnet2Subnet3788AAA1": {
|
304 | 304 | "Type": "AWS::EC2::Subnet",
|
305 | 305 | "Properties": {
|
306 |
| - "VpcId": { |
307 |
| - "Ref": "Vpc8378EB38" |
308 |
| - }, |
309 | 306 | "AvailabilityZone": {
|
310 | 307 | "Fn::Select": [
|
311 | 308 | 1,
|
|
329 | 326 | "Key": "Name",
|
330 | 327 | "Value": "integ-default-capacity-provider/Vpc/PrivateSubnet2"
|
331 | 328 | }
|
332 |
| - ] |
| 329 | + ], |
| 330 | + "VpcId": { |
| 331 | + "Ref": "Vpc8378EB38" |
| 332 | + } |
333 | 333 | }
|
334 | 334 | },
|
335 | 335 | "VpcPrivateSubnet2RouteTableA678073B": {
|
336 | 336 | "Type": "AWS::EC2::RouteTable",
|
337 | 337 | "Properties": {
|
338 |
| - "VpcId": { |
339 |
| - "Ref": "Vpc8378EB38" |
340 |
| - }, |
341 | 338 | "Tags": [
|
342 | 339 | {
|
343 | 340 | "Key": "Name",
|
344 | 341 | "Value": "integ-default-capacity-provider/Vpc/PrivateSubnet2"
|
345 | 342 | }
|
346 |
| - ] |
| 343 | + ], |
| 344 | + "VpcId": { |
| 345 | + "Ref": "Vpc8378EB38" |
| 346 | + } |
347 | 347 | }
|
348 | 348 | },
|
349 | 349 | "VpcPrivateSubnet2RouteTableAssociationA89CAD56": {
|
|
360 | 360 | "VpcPrivateSubnet2DefaultRoute060D2087": {
|
361 | 361 | "Type": "AWS::EC2::Route",
|
362 | 362 | "Properties": {
|
363 |
| - "RouteTableId": { |
364 |
| - "Ref": "VpcPrivateSubnet2RouteTableA678073B" |
365 |
| - }, |
366 | 363 | "DestinationCidrBlock": "0.0.0.0/0",
|
367 | 364 | "NatGatewayId": {
|
368 | 365 | "Ref": "VpcPublicSubnet2NATGateway9182C01D"
|
| 366 | + }, |
| 367 | + "RouteTableId": { |
| 368 | + "Ref": "VpcPrivateSubnet2RouteTableA678073B" |
369 | 369 | }
|
370 | 370 | }
|
371 | 371 | },
|
|
383 | 383 | "VpcVPCGWBF912B6E": {
|
384 | 384 | "Type": "AWS::EC2::VPCGatewayAttachment",
|
385 | 385 | "Properties": {
|
386 |
| - "VpcId": { |
387 |
| - "Ref": "Vpc8378EB38" |
388 |
| - }, |
389 | 386 | "InternetGatewayId": {
|
390 | 387 | "Ref": "VpcIGWD7BA715C"
|
| 388 | + }, |
| 389 | + "VpcId": { |
| 390 | + "Ref": "Vpc8378EB38" |
391 | 391 | }
|
392 | 392 | }
|
393 | 393 | },
|
|
627 | 627 | "ASG46ED3070": {
|
628 | 628 | "Type": "AWS::AutoScaling::AutoScalingGroup",
|
629 | 629 | "Properties": {
|
630 |
| - "MaxSize": "1", |
631 |
| - "MinSize": "1", |
632 | 630 | "LaunchTemplate": {
|
633 | 631 | "LaunchTemplateId": {
|
634 | 632 | "Ref": "ASGLaunchTemplate0CA92847"
|
|
640 | 638 | ]
|
641 | 639 | }
|
642 | 640 | },
|
| 641 | + "MaxSize": "1", |
| 642 | + "MinSize": "1", |
643 | 643 | "Tags": [
|
644 | 644 | {
|
645 | 645 | "Key": "Name",
|
|
792 | 792 | "Code": {
|
793 | 793 | "ZipFile": "import boto3, json, os, time\n\necs = boto3.client('ecs')\nautoscaling = boto3.client('autoscaling')\n\n\ndef lambda_handler(event, context):\n print(json.dumps(dict(event, ResponseURL='...')))\n cluster = os.environ['CLUSTER']\n snsTopicArn = event['Records'][0]['Sns']['TopicArn']\n lifecycle_event = json.loads(event['Records'][0]['Sns']['Message'])\n instance_id = lifecycle_event.get('EC2InstanceId')\n if not instance_id:\n print('Got event without EC2InstanceId: %s', json.dumps(dict(event, ResponseURL='...')))\n return\n\n instance_arn = container_instance_arn(cluster, instance_id)\n print('Instance %s has container instance ARN %s' % (lifecycle_event['EC2InstanceId'], instance_arn))\n\n if not instance_arn:\n return\n\n task_arns = container_instance_task_arns(cluster, instance_arn)\n\n if task_arns:\n print('Instance ARN %s has task ARNs %s' % (instance_arn, ', '.join(task_arns)))\n\n while has_tasks(cluster, instance_arn, task_arns):\n time.sleep(10)\n\n try:\n print('Terminating instance %s' % instance_id)\n autoscaling.complete_lifecycle_action(\n LifecycleActionResult='CONTINUE',\n **pick(lifecycle_event, 'LifecycleHookName', 'LifecycleActionToken', 'AutoScalingGroupName'))\n except Exception as e:\n # Lifecycle action may have already completed.\n print(str(e))\n\n\ndef container_instance_arn(cluster, instance_id):\n \"\"\"Turn an instance ID into a container instance ARN.\"\"\"\n arns = ecs.list_container_instances(cluster=cluster, filter='ec2InstanceId==' + instance_id)['containerInstanceArns']\n if not arns:\n return None\n return arns[0]\n\ndef container_instance_task_arns(cluster, instance_arn):\n \"\"\"Fetch tasks for a container instance ARN.\"\"\"\n arns = ecs.list_tasks(cluster=cluster, containerInstance=instance_arn)['taskArns']\n return arns\n\ndef has_tasks(cluster, instance_arn, task_arns):\n \"\"\"Return True if the instance is running tasks for the given cluster.\"\"\"\n instances = ecs.describe_container_instances(cluster=cluster, containerInstances=[instance_arn])['containerInstances']\n if not instances:\n return False\n instance = instances[0]\n\n if instance['status'] == 'ACTIVE':\n # Start draining, then try again later\n set_container_instance_to_draining(cluster, instance_arn)\n return True\n\n task_count = None\n\n if task_arns:\n # Fetch details for tasks running on the container instance\n tasks = ecs.describe_tasks(cluster=cluster, tasks=task_arns)['tasks']\n if tasks:\n # Consider any non-stopped tasks as running\n task_count = sum(task['lastStatus'] != 'STOPPED' for task in tasks) + instance['pendingTasksCount']\n\n if not task_count:\n # Fallback to instance task counts if detailed task information is unavailable\n task_count = instance['runningTasksCount'] + instance['pendingTasksCount']\n\n print('Instance %s has %s tasks' % (instance_arn, task_count))\n\n return task_count > 0\n\ndef set_container_instance_to_draining(cluster, instance_arn):\n ecs.update_container_instances_state(\n cluster=cluster,\n containerInstances=[instance_arn], status='DRAINING')\n\n\ndef pick(dct, *keys):\n \"\"\"Pick a subset of a dict.\"\"\"\n return {k: v for k, v in dct.items() if k in keys}\n"
|
794 | 794 | },
|
795 |
| - "Role": { |
796 |
| - "Fn::GetAtt": [ |
797 |
| - "ASGDrainECSHookFunctionServiceRoleC12963BB", |
798 |
| - "Arn" |
799 |
| - ] |
800 |
| - }, |
801 | 795 | "Environment": {
|
802 | 796 | "Variables": {
|
803 | 797 | "CLUSTER": {
|
|
806 | 800 | }
|
807 | 801 | },
|
808 | 802 | "Handler": "index.lambda_handler",
|
| 803 | + "Role": { |
| 804 | + "Fn::GetAtt": [ |
| 805 | + "ASGDrainECSHookFunctionServiceRoleC12963BB", |
| 806 | + "Arn" |
| 807 | + ] |
| 808 | + }, |
809 | 809 | "Runtime": "python3.9",
|
810 | 810 | "Tags": [
|
811 | 811 | {
|
|
839 | 839 | "ASGDrainECSHookFunctionTopicD6FC59F7": {
|
840 | 840 | "Type": "AWS::SNS::Subscription",
|
841 | 841 | "Properties": {
|
842 |
| - "Protocol": "lambda", |
843 |
| - "TopicArn": { |
844 |
| - "Ref": "ASGLifecycleHookDrainHookTopicA8AD4ACB" |
845 |
| - }, |
846 | 842 | "Endpoint": {
|
847 | 843 | "Fn::GetAtt": [
|
848 | 844 | "ASGDrainECSHookFunction5F24CF4D",
|
849 | 845 | "Arn"
|
850 | 846 | ]
|
| 847 | + }, |
| 848 | + "Protocol": "lambda", |
| 849 | + "TopicArn": { |
| 850 | + "Ref": "ASGLifecycleHookDrainHookTopicA8AD4ACB" |
851 | 851 | }
|
852 | 852 | }
|
853 | 853 | },
|
|
914 | 914 | "AutoScalingGroupName": {
|
915 | 915 | "Ref": "ASG46ED3070"
|
916 | 916 | },
|
917 |
| - "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", |
918 | 917 | "DefaultResult": "CONTINUE",
|
919 | 918 | "HeartbeatTimeout": 300,
|
| 919 | + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", |
920 | 920 | "NotificationTargetARN": {
|
921 | 921 | "Ref": "ASGLifecycleHookDrainHookTopicA8AD4ACB"
|
922 | 922 | },
|
|
940 | 940 | "Ref": "ASG46ED3070"
|
941 | 941 | },
|
942 | 942 | "ManagedScaling": {
|
| 943 | + "InstanceWarmupPeriod": 301, |
943 | 944 | "Status": "ENABLED",
|
944 | 945 | "TargetCapacity": 100
|
945 | 946 | },
|
|
0 commit comments