|
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": "aws-ecs-integ-ecs/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": "aws-ecs-integ-ecs/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 | "VpcPrivateSubnet1Subnet536B997A": {
|
127 | 127 | "Type": "AWS::EC2::Subnet",
|
128 | 128 | "Properties": {
|
129 |
| - "VpcId": { |
130 |
| - "Ref": "Vpc8378EB38" |
131 |
| - }, |
132 | 129 | "AvailabilityZone": {
|
133 | 130 | "Fn::Select": [
|
134 | 131 | 0,
|
|
152 | 149 | "Key": "Name",
|
153 | 150 | "Value": "aws-ecs-integ-ecs/Vpc/PrivateSubnet1"
|
154 | 151 | }
|
155 |
| - ] |
| 152 | + ], |
| 153 | + "VpcId": { |
| 154 | + "Ref": "Vpc8378EB38" |
| 155 | + } |
156 | 156 | }
|
157 | 157 | },
|
158 | 158 | "VpcPrivateSubnet1RouteTableB2C5B500": {
|
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": "aws-ecs-integ-ecs/Vpc/PrivateSubnet1"
|
168 | 165 | }
|
169 |
| - ] |
| 166 | + ], |
| 167 | + "VpcId": { |
| 168 | + "Ref": "Vpc8378EB38" |
| 169 | + } |
170 | 170 | }
|
171 | 171 | },
|
172 | 172 | "VpcPrivateSubnet1RouteTableAssociation70C59FA6": {
|
|
183 | 183 | "VpcPrivateSubnet1DefaultRouteBE02A9ED": {
|
184 | 184 | "Type": "AWS::EC2::Route",
|
185 | 185 | "Properties": {
|
186 |
| - "RouteTableId": { |
187 |
| - "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" |
188 |
| - }, |
189 | 186 | "DestinationCidrBlock": "0.0.0.0/0",
|
190 | 187 | "NatGatewayId": {
|
191 | 188 | "Ref": "VpcPublicSubnet1NATGateway4D7517AA"
|
| 189 | + }, |
| 190 | + "RouteTableId": { |
| 191 | + "Ref": "VpcPrivateSubnet1RouteTableB2C5B500" |
192 | 192 | }
|
193 | 193 | }
|
194 | 194 | },
|
|
206 | 206 | "VpcVPCGWBF912B6E": {
|
207 | 207 | "Type": "AWS::EC2::VPCGatewayAttachment",
|
208 | 208 | "Properties": {
|
209 |
| - "VpcId": { |
210 |
| - "Ref": "Vpc8378EB38" |
211 |
| - }, |
212 | 209 | "InternetGatewayId": {
|
213 | 210 | "Ref": "VpcIGWD7BA715C"
|
| 211 | + }, |
| 212 | + "VpcId": { |
| 213 | + "Ref": "Vpc8378EB38" |
214 | 214 | }
|
215 | 215 | }
|
216 | 216 | },
|
|
412 | 412 | "EcsClusterDefaultAutoScalingGroupASGC1A785DB": {
|
413 | 413 | "Type": "AWS::AutoScaling::AutoScalingGroup",
|
414 | 414 | "Properties": {
|
415 |
| - "MaxSize": "1", |
416 |
| - "MinSize": "1", |
417 | 415 | "LaunchTemplate": {
|
418 | 416 | "LaunchTemplateId": {
|
419 | 417 | "Ref": "EcsClusterDefaultAutoScalingGroupLaunchTemplate3719972A"
|
|
425 | 423 | ]
|
426 | 424 | }
|
427 | 425 | },
|
| 426 | + "MaxSize": "1", |
| 427 | + "MinSize": "1", |
428 | 428 | "Tags": [
|
429 | 429 | {
|
430 | 430 | "Key": "Name",
|
|
577 | 577 | "Code": {
|
578 | 578 | "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"
|
579 | 579 | },
|
580 |
| - "Role": { |
581 |
| - "Fn::GetAtt": [ |
582 |
| - "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", |
583 |
| - "Arn" |
584 |
| - ] |
585 |
| - }, |
586 | 580 | "Environment": {
|
587 | 581 | "Variables": {
|
588 | 582 | "CLUSTER": {
|
|
591 | 585 | }
|
592 | 586 | },
|
593 | 587 | "Handler": "index.lambda_handler",
|
| 588 | + "Role": { |
| 589 | + "Fn::GetAtt": [ |
| 590 | + "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionServiceRole94543EDA", |
| 591 | + "Arn" |
| 592 | + ] |
| 593 | + }, |
594 | 594 | "Runtime": "python3.9",
|
595 | 595 | "Tags": [
|
596 | 596 | {
|
|
624 | 624 | "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionTopic8F34E394": {
|
625 | 625 | "Type": "AWS::SNS::Subscription",
|
626 | 626 | "Properties": {
|
627 |
| - "Protocol": "lambda", |
628 |
| - "TopicArn": { |
629 |
| - "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" |
630 |
| - }, |
631 | 627 | "Endpoint": {
|
632 | 628 | "Fn::GetAtt": [
|
633 | 629 | "EcsClusterDefaultAutoScalingGroupDrainECSHookFunctionE17A5F5E",
|
634 | 630 | "Arn"
|
635 | 631 | ]
|
| 632 | + }, |
| 633 | + "Protocol": "lambda", |
| 634 | + "TopicArn": { |
| 635 | + "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4" |
636 | 636 | }
|
637 | 637 | }
|
638 | 638 | },
|
|
699 | 699 | "AutoScalingGroupName": {
|
700 | 700 | "Ref": "EcsClusterDefaultAutoScalingGroupASGC1A785DB"
|
701 | 701 | },
|
702 |
| - "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", |
703 | 702 | "DefaultResult": "CONTINUE",
|
704 | 703 | "HeartbeatTimeout": 300,
|
| 704 | + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING", |
705 | 705 | "NotificationTargetARN": {
|
706 | 706 | "Ref": "EcsClusterDefaultAutoScalingGroupLifecycleHookDrainHookTopicACD2D4A4"
|
707 | 707 | },
|
|
911 | 911 | "Ref": "ScheduledEc2TaskScheduledTaskDef56328BA4"
|
912 | 912 | }
|
913 | 913 | },
|
| 914 | + { |
| 915 | + "Action": "ecs:TagResource", |
| 916 | + "Effect": "Allow", |
| 917 | + "Resource": { |
| 918 | + "Fn::Join": [ |
| 919 | + "", |
| 920 | + [ |
| 921 | + "arn:aws:ecs:", |
| 922 | + { |
| 923 | + "Ref": "AWS::Region" |
| 924 | + }, |
| 925 | + ":*:task/", |
| 926 | + { |
| 927 | + "Ref": "EcsCluster97242B84" |
| 928 | + }, |
| 929 | + "/*" |
| 930 | + ] |
| 931 | + ] |
| 932 | + } |
| 933 | + }, |
914 | 934 | {
|
915 | 935 | "Action": "iam:PassRole",
|
916 | 936 | "Effect": "Allow",
|
|
0 commit comments