Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Higherings committed Feb 14, 2020
1 parent da0c93d commit ef5b3ae
Showing 3 changed files with 327 additions and 0 deletions.
220 changes: 220 additions & 0 deletions autoComputeOptimizer-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
# igarcia 2020-02
# Version 0.5
# Automation for Compute Optimizer Recommendations
# Create a Scheduled Lambda with CloudWatch Event to implements Compute Optimizer Recommendations changes on EC2 Instances
# Resources to create: Lambda, CloudWatch Event, IAM Role
# The AWS Account should be already signed up to AWS Compute Optimizer
# IMPORTANT: your EC2 instance should endure a restart!

AWSTemplateFormatVersion: 2010-09-09
Description: Template to automate the Compute Optimizer Recommendatios changes

Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: "Stack Environment"
Parameters:
- TemplateAmbiente
-
Label:
default: "Compute Optimizer Auto-Changes Setup"
Parameters:
- pType
- pRisk
- pFrecuencia
- pHora
- pTagBusqueda
- pTagValor
ParameterLabels: #Define Friendly Names
TemplateAmbiente:
default: "Value for CloudFormation Resources Env Tag"
pFrecuencia:
default: "Frequency, in days"
pHora:
default: "Time"
pType:
default: "Type of Recommendations"
pRisk:
default: "Tolerable Risk"
pTagBusqueda:
default: "Selection Tag Key, sets the Tag used to identified Instances for Automation of Compute Optimizer"
pTagValor:
default: "Selection Tag Value, sets the Value of the Tag to identified instances"

Parameters: #Default Parameters Values
TemplateAmbiente:
Type: String
Description: "Tag Env to set on CloudFormation resources"
Default: Test
MaxLength: 10
MinLength: 2
ConstraintDescription: "Set a simple Tag between 2 and 10 characters long"
pFrecuencia:
Type: Number
Description: "How often, in # days, the recommendations should be applied (7-30)"
Default: 14
MinValue: 7
MaxValue: 30
pHora:
Type: String
Description: "At what time, in 24 hours UTC time format ##:##, the Change will be applied"
Default: "6:01"
AllowedPattern: "^[0-2]?[0-9]:[0-5][0-9]$"
ConstraintDescription: "Please set a time between 0:00 a 23:59"
pRisk:
Type: Number
Description: "Set the tolerable Risk of the recommendation, 0 Very Low - 5 Very high, Default 2 (Low)"
Default: 2
MaxValue: 5
MinValue: 0
ConstraintDescription: "Please set a value between 0 and 5"
pType:
Type: String
Description: "Recommendations to apply, Underprovisioned and/or Overprovisioned"
Default: "Underprovisioned"
AllowedValues: ["Underprovisioned","Overprovisioned","Both"]
ConstraintDescription: "Set the recommendations to apply, you must choose at least one type."
pTagBusqueda:
Type: String
Description: "Tag Key that identify EC2 Instances for automation of Compute Optimizer"
Default: Optimized
MaxLength: 20
MinLength: 1
ConstraintDescription: "Set a simple Tag between 2 and 20 characters long"
pTagValor:
Type: String
Description: "Tag Value to identify EC2 Instances"
Default: Auto
MaxLength: 20
MinLength: 1
ConstraintDescription: "Please set a simple value without spaces"

Resources:
TemplateRole: #Rol para ejecucion de Lambda
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
Action:
- "sts:AssumeRole"
Description: Rol to execute autoComputeOptimizer
Policies:
-
PolicyName: !Join [ "-", [ "PolicyLambdaExec", !Ref TemplateAmbiente ] ]
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
-
PolicyName: !Join [ "-", [ "PolicyLambdaEC2", !Ref TemplateAmbiente ] ]
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action: "ec2:*"
Resource: "*"
-
PolicyName: !Join [ "-", [ "PolicyLambdaComputeOptimizer", !Ref TemplateAmbiente ] ]
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "compute-optimizer:*"
Resource: "*"
-
PolicyName: !Join [ "-", [ "PolicyLambdaKMS", !Ref TemplateAmbiente ] ]
PolicyDocument:
Version: "2012-10-17"
Statement:
-
Effect: "Allow"
Action:
- "kms:Encrypt"
- "kms:Decrypt"
- "kms:ReEncrypt*"
- "kms:GenerateDataKey*"
- "kms:DescribeKey"
- "kms:CreateGrant"
- "kms:ListGrants"
- "kms:RevokeGrant"
Resource: "*"
RoleName: !Join [ "-", [ "RolforAutoComputeOptimizer", !Ref TemplateAmbiente ] ]
Tags:
- Key: Env
Value: !Ref TemplateAmbiente

TemplateLambda: #Lambda que copia los backups
Type: AWS::Lambda::Function
DependsOn: TemplateRole
Properties:
Code:
S3Bucket: higher-artifacts
S3Key: "autocomputeoptimizer.zip"
Description: Implements the recommendations of AWS Compute Optimizer by automation
Environment:
Variables:
AMBIENTE: !Ref TemplateAmbiente
RISK: !Ref pRisk
TYPE: !Ref pType
TAGBUSQUEDA: !Ref pTagBusqueda
TAGVALOR: !Ref pTagValor
FunctionName: !Join [ "-", [ "AutoComputeOptimizerLambda", !Ref TemplateAmbiente ] ]
Role: !GetAtt TemplateRole.Arn
Timeout: 800
Handler: autocomputeoptimizer.lambda_handler
Runtime: python3.7
MemorySize: 256
Tags:
-
Key: Env
Value: !Ref TemplateAmbiente

TemplateEventLambda: #Crea Event para invocar la Lambda
Type: AWS::Events::Rule
DependsOn: TemplateLambda
Properties:
Description: Invoke Lambda for Compute Optimizer Automation
Name: !Join [ "-", [ "AutoComputeOptimizerEvent", !Ref TemplateAmbiente ] ]
ScheduleExpression: !Join [ "", [ "cron(",!Select [ 1, !Split [ ":", !Ref pHora] ]," ", !Select [ 0, !Split [ ":", !Ref pHora] ], " 1/",!Ref pFrecuencia," * ? *)" ] ]
State: ENABLED
Targets:
-
Arn: !GetAtt TemplateLambda.Arn
Id: !Join [ "-", [!Ref TemplateAmbiente, "AutoComputeOptimizerLambda" ] ]

TemplatePermisoEventLambda: #Relaciona permisos del Event con Lambda
Type: AWS::Lambda::Permission
DependsOn: TemplateEventLambda
Properties:
FunctionName: !GetAtt TemplateLambda.Arn
Action: "lambda:InvokeFunction"
Principal: events.amazonaws.com
SourceArn: !GetAtt TemplateEventLambda.Arn

Outputs: #Salida a mostrarse en la pantalla
FrequencyAutoCO:
Description: Frequency for Auto implement Compute Optimizer recommendations
Value: !Join [ "", [ "Every ", !Ref pFrecuencia, " days, at ", !Ref pHora, " UTC" ] ]
Recommendations:
Description: Type of recommendatios to implement automatically
Value: !Ref pType
TolerableRisk:
Description: Tolerable Risk for the Recommendations (0 = very low - 5 = very high)
Value: !Ref pRisk
107 changes: 107 additions & 0 deletions autocomputeoptimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# igarcia 2020-02
# Version 0.5
# Automation for Compute Optimizer Recommendations
# It will change the EC2 Instance Type to a Recommendation of the AWS Compute Optimizer Service
# It won't do anything to AutoScaling Group's Instances
# You can set a TAG Value for the instances that this Lambda can manage
# IMPORTANT: your EC2 instance should endure a restart!

import os
import json
import boto3

RISK = os.environ['RISK'] #de 0 a 5, 0 es sin riesgo y 5 es mucho riesgo (No Risk, Very Low, Low, Medium, High, Very High)
TYPE = os.environ['TYPE'] # Overprovisioned, Underprovisioned or Both
TAGBUSQUEDA = os.environ['TAGBUSQUEDA']
TAGVALOR = os.environ['TAGVALOR']

ec2 = boto3.resource('ec2')
co_client = boto3.client('compute-optimizer')

def review_compute_optimizer_recos(instance):
cambio = 0
response = ""
ec2_id = instance['instanceArn'].split('/')[1] #Instance ID
ec2_name = instance['instanceName']
ec2_prev_type = instance['currentInstanceType']
to_do = False # Flag to determine if Instance will be examined

ec2_instance = ec2.Instance(ec2_id)
ec2_tags = ec2_instance.tags
for tag in ec2_tags:
if tag['Key'] == TAGBUSQUEDA and tag['Value'] == TAGVALOR:
to_do = True

if to_do:
for option in instance['recommendationOptions']:
ec2_new_type = option['instanceType']
if (option['rank'] == 1) and (int(option['performanceRisk']) <= int(RISK)) and (ec2_prev_type != ec2_new_type): # Debe ser la 1ra opcion, el riesgo debe ser aceptable y el tipo de Instancia debe cambiar
#Hacer Cambio Tipo de Instancia
if ec2_instance.state['Name'] == 'stopped':
try:
response = ec2_instance.modify_attribute(InstanceType={'Value':ec2_new_type})
response = ec2_instance.start()
response = ec2_instance.wait_until_running()
cambio = 1
print("Se modificó Instancia {} - {} de {} a tipo {} ".format(ec2_id, ec2_name, ec2_prev_type, ec2_new_type))
except:
ec2_instance.stop()
ec2_instance.wait_until_stopped()
ec2_instance.modify_attribute(InstanceType={'Value':ec2_prev_type})
ec2_instance.start()
cambio = 0
print(response)
print("No se puedo modificar Instancia {} - {} a tipo {} ".format(ec2_id, ec2_name, ec2_new_type))
elif ec2_instance.state['Name'] == 'running':
try:
response = ec2_instance.stop()
response = ec2_instance.wait_until_stopped()
response = ec2_instance.modify_attribute(InstanceType={'Value':ec2_new_type})
response = ec2_instance.start()
response = ec2_instance.wait_until_running()
cambio = 1
print("Se modificó Instancia {} - {} de {} a tipo {} ".format(ec2_id, ec2_name, ec2_prev_type, ec2_new_type))
except:
ec2_instance.stop()
ec2_instance.wait_until_stopped()
ec2_instance.modify_attribute(InstanceType={'Value':ec2_prev_type})
ec2_instance.start()
cambio = 0
print(response)
print("No se puedo modificar Instancia {} - {} a tipo {} ".format(ec2_id, ec2_name, ec2_new_type))
break #Salgo del ciclo de OPCIONES
else:
print("No se modificó Instancia {} - {} debido a que no tiene el TAG necesario.".format(ec2_id, ec2_name))

return cambio

def lambda_handler(event, context):
total = 0
cambios = 0

if TYPE == "Both":
R_TYPE = ['Underprovisioned','Overprovisioned']
else:
R_TYPE = [TYPE]

co_recos = co_client.get_ec2_instance_recommendations(filters=[{'name':'Finding','values':R_TYPE}])
for instance in co_recos['instanceRecommendations']:
total+=1
cambios = cambios + review_compute_optimizer_recos(instance)

while 'nextToken' in co_recos: #Pagineo
co_recos = co_client.get_ec2_instance_recommendations(
nextToken=co_recos['nextToken'],
filters=[{'name':'Finding','values':R_TYPE}]
)
for instance in co_recos['instanceRecommendations']:
total+=1
cambios = cambios + review_compute_optimizer_recos(instance)

print("Se realizaron {} cambios con éxito de un total de {} sugeridos.".format(cambios,total))
return {
'statusCode': 200,
'body': json.dumps(
'Lambda finalizada exitosamente.'
)
}
Binary file added autocomputeoptimizer.zip
Binary file not shown.

0 comments on commit ef5b3ae

Please sign in to comment.