├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── cform └── ec2-scheduler.template ├── code └── ec2-scheduler.py └── documentation └── ec2-scheduler.pdf /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Amazon Software License 2 | This Amazon Software License ("License") governs your use, reproduction, and distribution of the accompanying software as specified below. 3 | 1. Definitions 4 | "Licensor" means any person or entity that distributes its Work. 5 | 6 | "Software" means the original work of authorship made available under this License. 7 | 8 | "Work" means the Software and any additions to or derivative works of the Software that are made available under this License. 9 | 10 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the meaning as provided under U.S. copyright law; provided, however, that for the purposes of this License, derivative works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work. 11 | 12 | Works, including the Software, are "made available" under this License by including in or with the Work either (a) a copyright notice referencing the applicability of this License to the Work, or (b) a copy of this License. 13 | 2. License Grants 14 | 2.1 Copyright Grant. Subject to the terms and conditions of this License, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free, copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense and distribute its Work and any resulting derivative works in any form. 15 | 2.2 Patent Grant. Subject to the terms and conditions of this License, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free patent license to make, have made, use, sell, offer for sale, import, and otherwise transfer its Work, in whole or in part. The foregoing license applies only to the patent claims licensable by Licensor that would be infringed by Licensor"s Work (or portion thereof) individually and excluding any combinations with any other materials or technology. 16 | 3. Limitations 17 | 3.1 Redistribution. You may reproduce or distribute the Work only if (a) you do so under this License, (b) you include a complete copy of this License with your distribution, and (c) you retain without modification any copyright, patent, trademark, or attribution notices that are present in the Work. 18 | 3.2 Derivative Works. You may specify that additional or different terms apply to the use, reproduction, and distribution of your derivative works of the Work ("Your Terms") only if (a) Your Terms provide that the use limitation in Section 3.3 applies to your derivative works, and (b) you identify the specific derivative works that are subject to Your Terms. Notwithstanding Your Terms, this License (including the redistribution requirements in Section 3.1) will continue to apply to the Work itself. 19 | 3.3 Use Limitation. The Work and any derivative works thereof only may be used or intended for use with the web services, computing platforms or applications provided by Amazon.com, Inc. or its affiliates, including Amazon Web Services, Inc. 20 | 3.4 Patent Claims. If you bring or threaten to bring a patent claim against any Licensor (including any claim, cross-claim or counterclaim in a lawsuit) to enforce any patents that you allege are infringed by any Work, then your rights under this License from such Licensor (including the grants in Sections 2.1 and 2.2) will terminate immediately. 21 | 3.5 Trademarks. This License does not grant any rights to use any Licensor"s or its affiliates" names, logos, or trademarks, except as necessary to reproduce the notices described in this License. 22 | 3.6 Termination. If you violate any term of this License, then your rights under this License (including the grants in Sections 2.1 and 2.2) will terminate immediately. 23 | 4. Disclaimer of Warranty. 24 | THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF M ERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER THIS LICENSE. SOME STATES" CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU. 25 | 5. Limitation of Liability. 26 | EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 27 | Effective Date - April 18, 2008 (Copyright) 2008 Amazon.com, Inc. or its affiliates. All rights reserved. -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | EC2 Scheduler 2 | Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notice 2 | EC2 Scheduler has been superseded by [AWS Instance Scheduler](https://aws.amazon.com/answers/infrastructure-management/instance-scheduler/). 3 | 4 | In 2016, the EC2 Scheduler was launched to help AWS customers easily configure custom start and stop schedules for their Amazon Elastic Compute Cloud (Amazon EC2) instances. In 2018, AWS launched [AWS Instance Scheduler](https://aws.amazon.com/answers/infrastructure-management/instance-scheduler/), a new and improved scheduling solution that enables customers to schedule Amazon EC2 instances, Amazon Relational Database Service (Amazon RDS) instances, and more. We encourage customers to migrate to AWS Instance Scheduler for future updates and new features. 5 | 6 | Legacy templates, scripts, and documentation for EC2 Scheduler are available in this GitHub repository. 7 | 8 | # ec2-scheduler 9 | 10 | EC2 Scheduler is a simple AWS-provided solution that enables customers to easily configure custom start and stop schedules for their Amazon EC2 instances. The solution is easy to deploy and can help reduce operational costs for both development and production environments. 11 | 12 | Source code for the AWS solution "EC2 Scheduler". 13 | 14 | 15 | ## Cloudformation templates 16 | 17 | - cform/ec2-scheduler.template 18 | 19 | ## Lambda source code 20 | 21 | - code/ec2-scheduler.py 22 | 23 | *** 24 | 25 | Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. 26 | 27 | Licensed under the Amazon Software License (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at 28 | 29 | http://aws.amazon.com/asl/ 30 | 31 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions and limitations under the License. 32 | -------------------------------------------------------------------------------- /cform/ec2-scheduler.template: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "(SO0002) - EC2 Scheduler: This template installs an opt-in version of the EC2 Scheduler for automatically starting and stopping EC2 instances.", 4 | "Parameters": { 5 | "Schedule": { 6 | "Description": "Schedule for CWE Scheduled Expression", 7 | "Type": "String", 8 | "Default": "5minutes", 9 | "AllowedValues": [ 10 | "1minute", 11 | "5minutes", 12 | "15minutes", 13 | "30minutes", 14 | "1hour" 15 | ] 16 | }, 17 | "DefaultStartTime": { 18 | "Description": "Default Start Time (UTC, 24-hour format)", 19 | "Type": "String", 20 | "Default": "0800" 21 | }, 22 | "DefaultStopTime": { 23 | "Description": "Default Start Time (UTC, 24-hour format)", 24 | "Type": "String", 25 | "Default": "1800" 26 | }, 27 | "DefaultDaysActive": { 28 | "Description": "Enter 'all', 'weekdays', or any combination of days ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', or 'sun') comma separated", 29 | "Type": "String", 30 | "Default": "all" 31 | }, 32 | "CustomTagName": { 33 | "Description": "Custom Tag Name", 34 | "Type": "String", 35 | "Default": "scheduler:ec2-startstop" 36 | }, 37 | "DynamoDBTableName": { 38 | "Description": "DynamoDB Table Name", 39 | "Type": "String", 40 | "Default": "EC2-Scheduler" 41 | }, 42 | "ReadCapacityUnits": { 43 | "ConstraintDescription": "should be between 5 and 10000", 44 | "Default": "1", 45 | "Description": "Provisioned read throughput", 46 | "MaxValue": "10000", 47 | "MinValue": "1", 48 | "Type": "Number" 49 | }, 50 | "WriteCapacityUnits": { 51 | "ConstraintDescription": "should be between 5 and 10000", 52 | "Default": "1", 53 | "Description": "Provisioned write throughput", 54 | "MaxValue": "10000", 55 | "MinValue": "1", 56 | "Type": "Number" 57 | }, 58 | "SendAnonymousData": { 59 | "Description": "Send anonymous data to AWS", 60 | "Type": "String", 61 | "Default": "Yes", 62 | "AllowedValues": [ 63 | "Yes", 64 | "No" 65 | ] 66 | }, 67 | "CloudWatchMetrics": { 68 | "Description": "Create CloudWatch Custom Metric", 69 | "Type": "String", 70 | "Default": "Enabled", 71 | "AllowedValues": [ 72 | "Enabled", 73 | "Disabled" 74 | ] 75 | } 76 | }, 77 | "Mappings": { 78 | "Schedule": { 79 | "Time": { 80 | "1minute": "cron(0/1 * * * ? *)", 81 | "5minutes": "cron(0/5 * * * ? *)", 82 | "15minutes": "cron(0/15 * * * ? *)", 83 | "30minutes": "cron(0/30 * * * ? *)", 84 | "1hour": "cron(0 * * * ? *)" 85 | } 86 | } 87 | }, 88 | "Metadata": { 89 | "AWS::CloudFormation::Interface": { 90 | "ParameterGroups": [ 91 | { 92 | "Label": { 93 | "default": "Tag Configuration" 94 | }, 95 | "Parameters": [ 96 | "CustomTagName" 97 | ] 98 | }, 99 | { 100 | "Label": { 101 | "default": "CloudWatch Event Schedule Configuration" 102 | }, 103 | "Parameters": [ 104 | "Schedule" 105 | ] 106 | }, 107 | { 108 | "Label": { 109 | "default": "Default Value Configuration" 110 | }, 111 | "Parameters": [ 112 | "DefaultStartTime", 113 | "DefaultStopTime", 114 | "DefaultDaysActive" 115 | ] 116 | }, 117 | { 118 | "Label": { 119 | "default": "DynamoDB Configuration" 120 | }, 121 | "Parameters": [ 122 | "DynamoDBTableName", 123 | "ReadCapacityUnits", 124 | "WriteCapacityUnits" 125 | ] 126 | }, 127 | { 128 | "Label": { 129 | "default": "CloudWatch Custom Metric" 130 | }, 131 | "Parameters": [ 132 | "CloudWatchMetrics" 133 | ] 134 | }, 135 | { 136 | "Label": { 137 | "default": "Anonymous Metrics Request" 138 | }, 139 | "Parameters": [ 140 | "SendAnonymousData" 141 | ] 142 | } 143 | ] 144 | } 145 | }, 146 | "Resources": { 147 | "ec2SchedulerRole": { 148 | "Type": "AWS::IAM::Role", 149 | "Properties": { 150 | "AssumeRolePolicyDocument": { 151 | "Version": "2012-10-17", 152 | "Statement": [ 153 | { 154 | "Effect": "Allow", 155 | "Principal": { 156 | "Service": "lambda.amazonaws.com" 157 | }, 158 | "Action": "sts:AssumeRole" 159 | } 160 | ] 161 | }, 162 | "Path": "/", 163 | "Policies": [ 164 | { 165 | "PolicyName": "ec2SchedulerPermissions", 166 | "PolicyDocument": { 167 | "Version": "2012-10-17", 168 | "Statement": [ 169 | { 170 | "Effect": "Allow", 171 | "Action": [ 172 | "logs:CreateLogGroup", 173 | "logs:CreateLogStream", 174 | "logs:PutLogEvents" 175 | ], 176 | "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*" 177 | }, 178 | { 179 | "Effect": "Allow", 180 | "Action": [ 181 | "dynamodb:GetItem" 182 | ], 183 | "Resource": [ 184 | "arn:aws:dynamodb:*:*:table/*" 185 | ] 186 | }, 187 | { 188 | "Effect": "Allow", 189 | "Action": [ 190 | "ec2:StartInstances", 191 | "ec2:StopInstances", 192 | "ec2:DescribeRegions", 193 | "ec2:DescribeInstances", 194 | "kms:CreateGrant", 195 | "cloudwatch:PutMetricData", 196 | "cloudformation:DescribeStacks" 197 | ], 198 | "Resource": "*" 199 | } 200 | ] 201 | } 202 | } 203 | ] 204 | } 205 | }, 206 | "ec2SchedulerOptIn": { 207 | "Type": "AWS::Lambda::Function", 208 | "Properties": { 209 | "Handler": "ec2-scheduler.lambda_handler", 210 | "Role": { 211 | "Fn::GetAtt": [ 212 | "ec2SchedulerRole", 213 | "Arn" 214 | ] 215 | }, 216 | "Description": "EC2 Scheduler Lambda function for automatically starting and stopping EC2 instances.", 217 | "Code": { 218 | "S3Bucket": { 219 | "Fn::Join": [ 220 | "", 221 | [ 222 | "solutions-", 223 | { 224 | "Ref": "AWS::Region" 225 | } 226 | ] 227 | ] 228 | }, 229 | "S3Key": "ec2-scheduler/v2/ec2-scheduler.zip" 230 | }, 231 | "Runtime": "python2.7", 232 | "Timeout": "300" 233 | } 234 | }, 235 | "CreateParamDDB": { 236 | "Properties": { 237 | "AttributeDefinitions": [ 238 | { 239 | "AttributeName": "SolutionName", 240 | "AttributeType": "S" 241 | } 242 | ], 243 | "KeySchema": [ 244 | { 245 | "AttributeName": "SolutionName", 246 | "KeyType": "HASH" 247 | } 248 | ], 249 | "ProvisionedThroughput": { 250 | "ReadCapacityUnits": { 251 | "Ref": "ReadCapacityUnits" 252 | }, 253 | "WriteCapacityUnits": { 254 | "Ref": "WriteCapacityUnits" 255 | } 256 | }, 257 | "TableName": { 258 | "Ref": "DynamoDBTableName" 259 | } 260 | }, 261 | "Type": "AWS::DynamoDB::Table" 262 | }, 263 | "SolutionHelperRole": { 264 | "Type": "AWS::IAM::Role", 265 | "Properties": { 266 | "AssumeRolePolicyDocument": { 267 | "Version": "2012-10-17", 268 | "Statement": [ 269 | { 270 | "Effect": "Allow", 271 | "Principal": { 272 | "Service": "lambda.amazonaws.com" 273 | }, 274 | "Action": "sts:AssumeRole" 275 | } 276 | ] 277 | }, 278 | "Path": "/", 279 | "Policies": [ 280 | { 281 | "PolicyName": "Solution_Helper_Permissions", 282 | "PolicyDocument": { 283 | "Version": "2012-10-17", 284 | "Statement": [ 285 | { 286 | "Effect": "Allow", 287 | "Action": [ 288 | "logs:CreateLogGroup", 289 | "logs:CreateLogStream", 290 | "logs:PutLogEvents" 291 | ], 292 | "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*" 293 | }, 294 | { 295 | "Effect": "Allow", 296 | "Action": [ 297 | "dynamodb:PutItem" 298 | ], 299 | "Resource": [ 300 | "arn:aws:dynamodb:*:*:table/*" 301 | ] 302 | }, 303 | { 304 | "Effect": "Allow", 305 | "Action": [ 306 | "lambda:AddPermission", 307 | "lambda:CreateFunction", 308 | "lambda:DeleteFunction", 309 | "lambda:GetFunction", 310 | "lambda:UpdateFunctionCode", 311 | "lambda:UpdateFunctionConfiguration", 312 | "s3:GetObject", 313 | "events:DeleteRule", 314 | "events:DisableRule", 315 | "events:EnableRule", 316 | "events:PutEvents", 317 | "events:PutRule", 318 | "events:PutTargets", 319 | "events:RemoveTargets", 320 | "events:ListTargetsByRule", 321 | "iam:PassRole" 322 | ], 323 | "Resource": "*" 324 | } 325 | ] 326 | } 327 | } 328 | ] 329 | } 330 | }, 331 | "SolutionHelper": { 332 | "Type": "AWS::Lambda::Function", 333 | "Properties": { 334 | "Handler": "solution-helper.lambda_handler", 335 | "Role": { 336 | "Fn::GetAtt": [ 337 | "SolutionHelperRole", 338 | "Arn" 339 | ] 340 | }, 341 | "Description": "This function creates a CloudFormation custom lambda resource that writes parameters into DynamoDB table.", 342 | "Code": { 343 | "S3Bucket": { 344 | "Fn::Join": [ 345 | "", 346 | [ 347 | "solutions-", 348 | { 349 | "Ref": "AWS::Region" 350 | } 351 | ] 352 | ] 353 | }, 354 | "S3Key": "library/solution-helper/v3/solution-helper.zip" 355 | }, 356 | "Runtime": "python2.7", 357 | "Timeout": "120" 358 | } 359 | }, 360 | "PutDdbData": { 361 | "Type": "Custom::PutDDBData", 362 | "Properties": { 363 | "ServiceToken": { 364 | "Fn::GetAtt": [ 365 | "SolutionHelper", 366 | "Arn" 367 | ] 368 | }, 369 | "StoreInDDB": { 370 | "Fn::Join": [ 371 | "", 372 | [ 373 | "{ 'TableName' : '", 374 | { 375 | "Ref": "CreateParamDDB" 376 | }, 377 | "', ", 378 | "'Item': {", 379 | "'CustomTagName': {'S': '", 380 | { 381 | "Ref": "CustomTagName" 382 | }, 383 | "'},", 384 | "'SolutionName': {'S': 'EC2Scheduler'},", 385 | "'DefaultStartTime': {'S': '", 386 | { 387 | "Ref": "DefaultStartTime" 388 | }, 389 | "'},", 390 | "'DefaultStopTime': {'S': '", 391 | { 392 | "Ref": "DefaultStopTime" 393 | }, 394 | "'},", 395 | "'SendAnonymousData': {'S': '", 396 | { 397 | "Ref": "SendAnonymousData" 398 | }, 399 | "'},", 400 | "'CloudWatchMetrics': {'S': '", 401 | { 402 | "Ref": "CloudWatchMetrics" 403 | }, 404 | "'},", 405 | "'UUID': {'S': '", 406 | { 407 | "Fn::GetAtt": [ 408 | "CreateUniqueID", 409 | "UUID" 410 | ] 411 | }, 412 | "'},", 413 | "'DefaultDaysActive': {'S': '", 414 | { 415 | "Ref": "DefaultDaysActive" 416 | }, 417 | "'}", 418 | "}", 419 | "}" 420 | ] 421 | ] 422 | }, 423 | "DependsOn": [ 424 | "CreateUniqueID", 425 | "CreateParamDDB" 426 | ] 427 | } 428 | }, 429 | "CreateUniqueID": { 430 | "Type": "Custom::CreateUUID", 431 | "Properties": { 432 | "ServiceToken": { 433 | "Fn::GetAtt": [ 434 | "SolutionHelper", 435 | "Arn" 436 | ] 437 | }, 438 | "Region": { 439 | "Ref": "AWS::Region" 440 | }, 441 | "CreateUniqueID": "true", 442 | "DependsOn": [ 443 | "SolutionHelper" 444 | ] 445 | } 446 | }, 447 | "ScheduledRule": { 448 | "Type": "AWS::Events::Rule", 449 | "Properties": { 450 | "Description": "Rule to trigger EC2Scheduler function on a schedule", 451 | "ScheduleExpression": { 452 | "Fn::FindInMap": [ 453 | "Schedule", 454 | "Time", 455 | { 456 | "Ref": "Schedule" 457 | } 458 | ] 459 | }, 460 | "State": "ENABLED", 461 | "Targets": [ 462 | { 463 | "Arn": { 464 | "Fn::GetAtt": [ 465 | "ec2SchedulerOptIn", 466 | "Arn" 467 | ] 468 | }, 469 | "Id": "TargetFunctionV1" 470 | } 471 | ] 472 | } 473 | }, 474 | "PermissionForEventsToInvokeLambda": { 475 | "Type": "AWS::Lambda::Permission", 476 | "Properties": { 477 | "FunctionName": { 478 | "Ref": "ec2SchedulerOptIn" 479 | }, 480 | "Action": "lambda:InvokeFunction", 481 | "Principal": "events.amazonaws.com", 482 | "SourceArn": { 483 | "Fn::GetAtt": [ 484 | "ScheduledRule", 485 | "Arn" 486 | ] 487 | } 488 | } 489 | } 490 | }, 491 | "Outputs": { 492 | "UUID": { 493 | "Description": "Newly created random UUID.", 494 | "Value": { 495 | "Fn::GetAtt": [ 496 | "CreateUniqueID", 497 | "UUID" 498 | ] 499 | } 500 | }, 501 | "DDBTableName": { 502 | "Description": "DynamoDB Table Name", 503 | "Value": { 504 | "Ref": "CreateParamDDB" 505 | } 506 | } 507 | } 508 | } -------------------------------------------------------------------------------- /code/ec2-scheduler.py: -------------------------------------------------------------------------------- 1 | ###################################################################################################################### 2 | # Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. # 3 | # # 4 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in compliance # 5 | # with the License. A copy of the License is located at # 6 | # # 7 | # http://aws.amazon.com/asl/ # 8 | # # 9 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES # 10 | # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # 11 | # and limitations under the License. # 12 | ###################################################################################################################### 13 | 14 | 15 | import boto3 16 | import datetime 17 | import json 18 | from urllib2 import Request 19 | from urllib2 import urlopen 20 | from collections import Counter 21 | 22 | def putCloudWatchMetric(region, instance_id, instance_state): 23 | 24 | cw = boto3.client('cloudwatch') 25 | 26 | cw.put_metric_data( 27 | Namespace='EC2Scheduler', 28 | MetricData=[{ 29 | 'MetricName': instance_id, 30 | 'Value': instance_state, 31 | 32 | 'Unit': 'Count', 33 | 'Dimensions': [ 34 | { 35 | 'Name': 'Region', 36 | 'Value': region 37 | } 38 | ] 39 | }] 40 | 41 | ) 42 | 43 | def lambda_handler(event, context): 44 | 45 | print "Running EC2 Scheduler" 46 | 47 | ec2 = boto3.client('ec2') 48 | cf = boto3.client('cloudformation') 49 | outputs = {} 50 | stack_name = context.invoked_function_arn.split(':')[6].rsplit('-', 2)[0] 51 | response = cf.describe_stacks(StackName=stack_name) 52 | for e in response['Stacks'][0]['Outputs']: 53 | outputs[e['OutputKey']] = e['OutputValue'] 54 | ddbTableName = outputs['DDBTableName'] 55 | 56 | awsRegions = ec2.describe_regions()['Regions'] 57 | dynamodb = boto3.resource('dynamodb') 58 | table = dynamodb.Table(ddbTableName) 59 | response = table.get_item( 60 | Key={ 61 | 'SolutionName': 'EC2Scheduler' 62 | } 63 | ) 64 | item = response['Item'] 65 | 66 | 67 | 68 | # Reading Default Values from DynamoDB 69 | customTagName = str(item['CustomTagName']) 70 | customTagLen = len(customTagName) 71 | defaultStartTime = str(item['DefaultStartTime']) 72 | defaultStopTime = str(item['DefaultStopTime']) 73 | defaultTimeZone = 'utc' 74 | defaultDaysActive = str(item['DefaultDaysActive']) 75 | sendData = str(item['SendAnonymousData']).lower() 76 | createMetrics = str(item['CloudWatchMetrics']).lower() 77 | UUID = str(item['UUID']) 78 | TimeNow = datetime.datetime.utcnow().isoformat() 79 | TimeStamp = str(TimeNow) 80 | 81 | # Declare Dicts 82 | regionDict = {} 83 | allRegionDict = {} 84 | regionsLabelDict = {} 85 | postDict = {} 86 | 87 | for region in awsRegions: 88 | try: 89 | # Create connection to the EC2 using Boto3 resources interface 90 | ec2 = boto3.resource('ec2', region_name=region['RegionName']) 91 | 92 | awsregion = region['RegionName'] 93 | now = datetime.datetime.now().strftime("%H%M") 94 | nowMax = datetime.datetime.now() - datetime.timedelta(minutes=59) 95 | nowMax = nowMax.strftime("%H%M") 96 | nowDay = datetime.datetime.today().strftime("%a").lower() 97 | 98 | # Declare Lists 99 | startList = [] 100 | stopList = [] 101 | runningStateList = [] 102 | stoppedStateList = [] 103 | 104 | # List all instances 105 | instances = ec2.instances.all() 106 | 107 | print "Creating", region['RegionName'], "instance lists..." 108 | 109 | for i in instances: 110 | if i.tags != None: 111 | for t in i.tags: 112 | if t['Key'][:customTagLen] == customTagName: 113 | 114 | ptag = t['Value'].split(";") 115 | 116 | # Split out Tag & Set Variables to default 117 | default1 = 'default' 118 | default2 = 'true' 119 | startTime = defaultStartTime 120 | stopTime = defaultStopTime 121 | timeZone = defaultTimeZone 122 | daysActive = defaultDaysActive 123 | state = i.state['Name'] 124 | itype = i.instance_type 125 | 126 | # Post current state of the instances 127 | if createMetrics == 'enabled': 128 | if state == "running": 129 | putCloudWatchMetric(region['RegionName'], i.instance_id, 1) 130 | if state == "stopped": 131 | putCloudWatchMetric(region['RegionName'], i.instance_id, 0) 132 | 133 | # Parse tag-value 134 | if len(ptag) >= 1: 135 | if ptag[0].lower() in (default1, default2): 136 | startTime = defaultStartTime 137 | else: 138 | startTime = ptag[0] 139 | stopTime = ptag[0] 140 | if len(ptag) >= 2: 141 | stopTime = ptag[1] 142 | if len(ptag) >= 3: 143 | timeZone = ptag[2].lower() 144 | if len(ptag) >= 4: 145 | daysActive = ptag[3].lower() 146 | 147 | isActiveDay = False 148 | 149 | # Days Interpreter 150 | if daysActive == "all": 151 | isActiveDay = True 152 | elif daysActive == "weekdays": 153 | weekdays = ['mon', 'tue', 'wed', 'thu', 'fri'] 154 | if (nowDay in weekdays): 155 | isActiveDay = True 156 | else: 157 | daysActive = daysActive.split(",") 158 | for d in daysActive: 159 | if d.lower() == nowDay: 160 | isActiveDay = True 161 | 162 | # Append to start list 163 | if startTime >= str(nowMax) and startTime <= str(now) and \ 164 | isActiveDay == True and state == "stopped": 165 | startList.append(i.instance_id) 166 | print i.instance_id, " added to START list" 167 | if createMetrics == 'enabled': 168 | putCloudWatchMetric(region['RegionName'], i.instance_id, 1) 169 | 170 | # Append to stop list 171 | if stopTime >= str(nowMax) and stopTime <= str(now) and \ 172 | isActiveDay == True and state == "running": 173 | stopList.append(i.instance_id) 174 | print i.instance_id, " added to STOP list" 175 | if createMetrics == 'enabled': 176 | putCloudWatchMetric(region['RegionName'], i.instance_id, 0) 177 | 178 | if state == 'running': 179 | runningStateList.append(itype) 180 | if state == 'stopped': 181 | stoppedStateList.append(itype) 182 | 183 | # Execute Start and Stop Commands 184 | if startList: 185 | print "Starting", len(startList), "instances", startList 186 | ec2.instances.filter(InstanceIds=startList).start() 187 | else: 188 | print "No Instances to Start" 189 | 190 | if stopList: 191 | print "Stopping", len(stopList) ,"instances", stopList 192 | ec2.instances.filter(InstanceIds=stopList).stop() 193 | else: 194 | print "No Instances to Stop" 195 | 196 | 197 | # Built payload for each region 198 | if sendData == "yes": 199 | countRunDict = {} 200 | typeRunDict = {} 201 | countStopDict = {} 202 | typeStopDict = {} 203 | runDictType = {} 204 | stopDictType = {} 205 | runDict = dict(Counter(runningStateList)) 206 | for k, v in runDict.iteritems(): 207 | countRunDict['Count'] = v 208 | typeRunDict[k] = countRunDict['Count'] 209 | 210 | stopDict = dict(Counter(stoppedStateList)) 211 | for k, v in stopDict.iteritems(): 212 | countStopDict['Count'] = v 213 | typeStopDict[k] = countStopDict['Count'] 214 | 215 | runDictType['instance_type'] = typeRunDict 216 | stopDictType['instance_type'] = typeStopDict 217 | 218 | typeStateSum = {} 219 | typeStateSum['running'] = runDictType 220 | typeStateSum['stopped'] = stopDictType 221 | StateSum = {} 222 | StateSum['instance_state'] = typeStateSum 223 | regionDict[awsregion] = StateSum 224 | allRegionDict.update(regionDict) 225 | 226 | except Exception as e: 227 | print ("Exception: "+str(e)) 228 | continue 229 | 230 | # Build payload for the account 231 | if sendData == "yes": 232 | regionsLabelDict['regions'] = allRegionDict 233 | postDict['Data'] = regionsLabelDict 234 | postDict['TimeStamp'] = TimeStamp 235 | postDict['Solution'] = 'SO0002' 236 | postDict['UUID'] = UUID 237 | # API Gateway URL to make HTTP POST call 238 | url = 'https://metrics.awssolutionsbuilder.com/generic' 239 | data=json.dumps(postDict) 240 | headers = {'content-type': 'application/json'} 241 | req = Request(url, data, headers) 242 | rsp = urlopen(req) 243 | content = rsp.read() 244 | rsp_code = rsp.getcode() 245 | print ('Response Code: {}'.format(rsp_code)) 246 | 247 | -------------------------------------------------------------------------------- /documentation/ec2-scheduler.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amazon-archives/ec2-scheduler/09733313bfe1a7c5e9d61e2fa0be70c54d89b1ad/documentation/ec2-scheduler.pdf --------------------------------------------------------------------------------