Last active
June 6, 2021 12:48
-
-
Save k4kratik/0bb1b8df9d7042f808a0111b637e2bd4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: "2010-09-09" | |
Description: Schedule automatic deletion of CloudFormation stacks | |
# Advance way to customize our Parameters inputs, looks very good to the users :) | |
Metadata: | |
License: Apache-2.0 | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- Label: | |
default: TTL configuration | |
Parameters: | |
- TTL | |
- Label: | |
default: Original CF configuration | |
Parameters: | |
- InstanceType | |
- KeyName | |
- LatestAmiId | |
- SSHLocation | |
ParameterLabels: | |
TTL: | |
default: Time-to-live | |
InstanceType: | |
default: Instance Type | |
KeyName: | |
default: Private Key Pair Name | |
LatestAmiId: | |
default: Latest AMI ID | |
SSHLocation: | |
default: SSH Allowed IP | |
Parameters: | |
TTL: | |
Type: Number | |
Description: Time-to-live in minutes for the stack. | |
InstanceType: | |
Description: WebServer EC2 instance type | |
Type: String | |
Default: t3.small | |
AllowedValues: | |
[ | |
t2.nano, | |
t2.micro, | |
t2.small, | |
t2.medium, | |
t2.large, | |
t2.xlarge, | |
t2.2xlarge, | |
t3.nano, | |
t3.micro, | |
t3.small, | |
t3.medium, | |
t3.large, | |
t3.xlarge, | |
t3.2xlarge, | |
m4.large, | |
m4.xlarge, | |
m4.2xlarge, | |
m4.4xlarge, | |
m4.10xlarge, | |
m5.large, | |
m5.xlarge, | |
m5.2xlarge, | |
m5.4xlarge, | |
c5.large, | |
c5.xlarge, | |
c5.2xlarge, | |
c5.4xlarge, | |
c5.9xlarge, | |
g3.8xlarge, | |
r5.large, | |
r5.xlarge, | |
r5.2xlarge, | |
r5.4xlarge, | |
r3.12xlarge, | |
i3.xlarge, | |
i3.2xlarge, | |
i3.4xlarge, | |
i3.8xlarge, | |
d2.xlarge, | |
d2.2xlarge, | |
d2.4xlarge, | |
d2.8xlarge, | |
] | |
ConstraintDescription: must be a valid EC2 instance type. | |
KeyName: | |
Description: Name of an existing EC2 KeyPair to enable SSH access to the instances | |
Type: AWS::EC2::KeyPair::KeyName | |
ConstraintDescription: must be the name of an existing EC2 KeyPair. | |
SSHLocation: | |
Description: The IP address range that can be used to SSH to the EC2 instances | |
Type: String | |
MinLength: "9" | |
MaxLength: "18" | |
Default: 0.0.0.0/0 | |
AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) | |
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. | |
LatestAmiId: | |
Type: "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>" | |
Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" | |
# We will define all resources first which we need for deletion to work, then we will define our actual resources. | |
# Resources used for auto deletion feature - | |
# - DeleteCFNLambdaExecutionRole, DeleteCFNLambda, PermissionForDeleteCFNLambda, DeleteStackEventRule (RESOURCES FOR DELETION) | |
# - BasicLambdaExecutionRole, GenerateCronExpLambda, GenerateCronExpression (RESOURCES FOR CRON EXPRESSION GENERATION) | |
Resources: | |
# RESOURCES FOR CRON EXPRESSION GENERATION - START | |
BasicLambdaExecutionRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Principal: | |
Service: ["lambda.amazonaws.com"] | |
Action: "sts:AssumeRole" | |
Path: "/" | |
Policies: | |
- PolicyName: "lambda_policy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- "logs:CreateLogGroup" | |
- "logs:CreateLogStream" | |
- "logs:PutLogEvents" | |
Resource: "arn:aws:logs:*:*:*" | |
GenerateCronExpLambda: | |
Type: "AWS::Lambda::Function" | |
Properties: | |
Code: | |
ZipFile: | | |
from datetime import datetime, timedelta | |
import os | |
import logging | |
import json | |
import cfnresponse | |
def deletion_time(ttl): | |
delete_at_time = datetime.now() + timedelta(minutes=int(ttl)) | |
hh = delete_at_time.hour | |
mm = delete_at_time.minute | |
yyyy = delete_at_time.year | |
month = delete_at_time.month | |
dd = delete_at_time.day | |
# minutes hours day month day-of-week year | |
cron_exp = "cron({} {} {} {} ? {})".format(mm, hh, dd, month, yyyy) | |
return cron_exp | |
def handler(event, context): | |
print('Received event: %s' % json.dumps(event)) | |
status = cfnresponse.SUCCESS | |
try: | |
if event['RequestType'] == 'Delete': | |
cfnresponse.send(event, context, status, {}) | |
else: | |
ttl = event['ResourceProperties']['ttl'] | |
responseData = {} | |
responseData['cron_exp'] = deletion_time(ttl) | |
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) | |
except Exception as e: | |
logging.error('Exception: %s' % e, exc_info=True) | |
status = cfnresponse.FAILED | |
cfnresponse.send(event, context, status, {}, None) | |
Handler: "index.handler" | |
Runtime: "python3.6" | |
Timeout: "5" | |
Role: !GetAtt BasicLambdaExecutionRole.Arn | |
GenerateCronExpression: | |
Type: "Custom::GenerateCronExpression" | |
Version: "1.0" | |
Properties: | |
ServiceToken: !GetAtt GenerateCronExpLambda.Arn | |
ttl: !Ref "TTL" | |
# RESOURCES FOR CRON EXPRESSION GENERATION - END | |
# RESOURCES FOR DELETION - START | |
DeleteCFNLambdaExecutionRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Principal: | |
Service: ["lambda.amazonaws.com"] | |
Action: "sts:AssumeRole" | |
Path: "/" | |
#! I have added this for simplicity, Avoid granting Admin Access to your role, instead fine tune what permission it needs to deleting resources mentioned in the CF Template. | |
ManagedPolicyArns: | |
- "arn:aws:iam::aws:policy/AdministratorAccess" #! CAUTION | |
Policies: | |
- PolicyName: "lambda_policy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- "logs:CreateLogGroup" | |
- "logs:CreateLogStream" | |
- "logs:PutLogEvents" | |
Resource: "arn:aws:logs:*:*:*" | |
- Effect: "Allow" | |
Action: | |
- "cloudformation:DeleteStack" | |
Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*" | |
DeleteCFNLambda: | |
Type: "AWS::Lambda::Function" | |
DependsOn: | |
- DeleteCFNLambdaExecutionRole | |
Properties: | |
FunctionName: !Sub "DeleteCFNLambda-${AWS::StackName}" | |
Code: | |
ZipFile: | | |
import boto3 | |
import os | |
import json | |
stack_name = os.environ['stackName'] | |
def delete_cfn(stack_name): | |
try: | |
cfn = boto3.resource('cloudformation') | |
stack = cfn.Stack(stack_name) | |
stack.delete() | |
return "SUCCESS" | |
except: | |
return "ERROR" | |
def handler(event, context): | |
print("Received event:") | |
print(json.dumps(event)) | |
return delete_cfn(stack_name) | |
Environment: | |
Variables: | |
stackName: !Ref "AWS::StackName" | |
Handler: "index.handler" | |
Runtime: "python3.6" | |
Timeout: "5" | |
Role: !GetAtt DeleteCFNLambdaExecutionRole.Arn | |
DeleteStackEventRule: | |
DependsOn: | |
- DeleteCFNLambda | |
- GenerateCronExpression | |
Type: "AWS::Events::Rule" | |
Properties: | |
Description: Delete stack event | |
ScheduleExpression: !GetAtt GenerateCronExpression.cron_exp | |
State: "ENABLED" | |
Targets: | |
- Arn: !GetAtt DeleteCFNLambda.Arn | |
Id: "DeleteCFNLambda" | |
PermissionForDeleteCFNLambda: | |
Type: "AWS::Lambda::Permission" | |
Properties: | |
FunctionName: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:DeleteCFNLambda-${AWS::StackName}" | |
Action: "lambda:InvokeFunction" | |
Principal: "events.amazonaws.com" | |
SourceArn: !GetAtt DeleteStackEventRule.Arn | |
# RESOURCES FOR DELETION - END | |
#? Actual Resources I wanted to created Originally - START | |
EC2Instance: | |
Type: AWS::EC2::Instance | |
Properties: | |
InstanceType: !Ref "InstanceType" | |
SecurityGroups: [!Ref "InstanceSecurityGroup"] | |
KeyName: !Ref "KeyName" | |
ImageId: !Ref "LatestAmiId" | |
InstanceSecurityGroup: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: Enable SSH access | |
SecurityGroupIngress: | |
- IpProtocol: tcp | |
FromPort: "22" | |
ToPort: "22" | |
CidrIp: !Ref "SSHLocation" | |
IPAddress: | |
Type: AWS::EC2::EIP | |
IPAssoc: | |
Type: AWS::EC2::EIPAssociation | |
Properties: | |
InstanceId: !Ref "EC2Instance" | |
EIP: !Ref "IPAddress" | |
#? Actual Resources - END | |
Outputs: | |
InstanceId: | |
Description: InstanceId of the newly created EC2 instance | |
Value: !Ref "EC2Instance" | |
InstanceIPAddress: | |
Description: IP address of the newly created EC2 instance | |
Value: !GetAtt EC2Instance.PublicIp |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment