Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
#
# Tutorial CloudFormation Template
#
# This template will provision a new Virtual Private Cloud and split it into a
# public and private subnet. A web server will be provisioned in the public
# subnet and a database server will be provisioned in the private subnet.
#
Description: Provisions a simple cluster with a web and database server
Parameters:
KeyPairName:
Type: String
Description: The name of the keypair used when provisioning instances
InstanceType:
Type: String
Description: Instance type to provision
Default: t2.micro
ImageId:
Type: String
Description: Image to use when provisioning instances
Default: ami-035be7bafff33b6b6 # Amazon Linux 2
VolumeSize:
Type: Number
Description: Size of the volume in GB
Default: 250
RootDevice:
Type: String
Description: Name of the root device on provisioned instances
Default: xvda # varies based on Linux type (Ubunut /dev/sda1)
RootFsType:
Type: String
Description: File system type for root device
Default: xfs
Resources:
#
# Virtual Private Network to hold our resources
#
TutorialVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.6.0.0/24"
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: "Name"
Value: "Tutorial VPC"
#
# Internet Gateway
#
TutorialInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: "Name"
Value: "Internet Gateway"
TutorialVPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref TutorialVPC
InternetGatewayId: !Ref TutorialInternetGateway
#
# Subnets
#
TutorialPublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref TutorialVPC
CidrBlock: "10.6.0.0/25"
MapPublicIpOnLaunch: true
Tags:
- Key: "Name"
Value: "Public Subnet"
TutorialPrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref TutorialVPC
CidrBlock: "10.6.0.128/25"
MapPublicIpOnLaunch: false
Tags:
- Key: "Name"
Value: "Private Subnet"
#
# Routing
#
TutorialPublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref TutorialVPC
Tags:
- Key: "Name"
Value: "Public Internet Route Table"
TutorialPrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref TutorialVPC
Tags:
- Key: "Name"
Value: "Private Route Table"
TutorialInternetRoute:
Type: AWS::EC2::Route
DependsOn: TutorialVPCGatewayAttachment
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref TutorialInternetGateway
RouteTableId: !Ref TutorialPublicRouteTable
TutorialNatGatewaySubnetRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref TutorialVPC
Tags:
- Key: "Name"
Value: "NAT Gateway Subnet Route Table"
TutorialNatGatewayInternetRoute:
Type: AWS::EC2::Route
DependsOn: TutorialVPCGatewayAttachment
Properties:
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref TutorialInternetGateway
RouteTableId: !Ref TutorialNatGatewaySubnetRouteTable
TutorialNatGatewayElasticIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
TutorialNatGateway:
Type: AWS::EC2::NatGateway
DependsOn: TutorialVPCGatewayAttachment
Properties:
AllocationId: !Sub ${TutorialNatGatewayElasticIP.AllocationId}
SubnetId: !Ref TutorialPublicSubnet
Tags:
- Key: "Name"
Value: "NAT Gateway"
TutorialPrivateSubnetNatRoute:
Type: AWS::EC2::Route
DependsOn: TutorialNatGateway
Properties:
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref TutorialNatGateway
RouteTableId: !Ref TutorialPrivateRouteTable
TutorialPublicSubnetRouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref TutorialPublicRouteTable
SubnetId: !Ref TutorialPublicSubnet
TutorialPrivateSubnetRouteAssoc:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref TutorialPrivateRouteTable
SubnetId: !Ref TutorialPrivateSubnet
#
# Security Groups
#
TutorialPublicSecurityGroup:
Type: AWS::EC2::SecurityGroup
Description: "Public Security Group"
Properties:
VpcId: !Ref TutorialVPC
GroupDescription: Web Server Security Group
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: 80 # HTTP
ToPort: 80
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: 443 # HTTPS
ToPort: 443
- CidrIp: 0.0.0.0/0 # Replace with your IP Range
IpProtocol: tcp
FromPort: 3389 # MS Remote Desktop
ToPort: 3389
- CidrIp: 0.0.0.0/0 # Replace with your IP Range
IpProtocol: tcp
FromPort: 22 # SSH
ToPort: 22
- CidrIp: 10.6.0.0/24 # Inside the VPC
IpProtocol: -1 # All traffic
SecurityGroupEgress:
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: 80 # HTTP
ToPort: 80
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: 443 # HTTPS
ToPort: 443
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: 587 # SSL SMTP
ToPort: 587
- CidrIp: 10.6.0.0/24 # Inside the VPC
IpProtocol: -1 # All traffic
Tags:
- Key: "Name"
Value: "Public Subnet"
TutorialPrivateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Description: "Private Security Group"
Properties:
VpcId: !Ref TutorialVPC
GroupDescription: Private Subnet Security Group
SecurityGroupIngress:
- CidrIp: 10.6.0.0/24 # Inside the VPC
IpProtocol: -1 # All traffic
SecurityGroupEgress:
- CidrIp: 10.6.0.0/24 # Inside the VPC
IpProtocol: -1 # All traffic
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: '80' # HTTP
ToPort: '80'
- CidrIp: 0.0.0.0/0 # Public Internet
IpProtocol: tcp
FromPort: '443' # HTTPS
ToPort: '443'
Tags:
- Key: "Name"
Value: "Private Subnet"
#
# S3 Bucket for Backups
#
TutorialBackupS3Bucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Properties:
AccessControl: Private
Tags:
- Key: "Name"
Value: "Backup Bucket"
#
# Administrator IAM Group
#
TutorialAdminGroup:
Type: AWS::IAM::Group
Properties:
Policies:
- PolicyName: TutorialAdminPolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:ListBucketByTags
- s3:ListBucketMultipartUploads
- s3:ListBucketVersions
Resource: !Sub ${TutorialBackupS3Bucket.Arn}
- Effect: Allow
Action:
- s3:PutObject
- s3:DeleteObject
- s3:GetObject
Resource: !Sub ${TutorialBackupS3Bucket.Arn}/*
#
# Role and Policies for Instances
#
TutorialInstanceRole:
Type: AWS::IAM::Role
Properties:
ManagedPolicyArns:
- arn:aws-us-gov:iam::aws:policy/CloudWatchAgentServerPolicy # Allow CloudWatch Agent to run on servers
- arn:aws-us-gov:iam::aws:policy/service-role/AmazonEC2RoleforSSM # Allow SSM Agent Access to CloudWatch
- arn:aws-us-gov:iam::aws:policy/AmazonSSMReadOnlyAccess # Allow instance to read SSM parameters
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
TutorialInstanceRolePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: TutorialInstanceRolePolicy
Roles:
- Ref: TutorialInstanceRole
PolicyDocument:
Statement:
- Effect: Allow
Action:
- s3:ListBucket
- s3:ListBucketByTags
- s3:ListBucketMultipartUploads
- s3:ListBucketVersions
Resource: !Sub ${TutorialBackupS3Bucket.Arn}
- Effect: Allow
Action:
- s3:PutObject
- s3:DeleteObject
- s3:GetObject
Resource: !Sub ${TutorialBackupS3Bucket.Arn}/*
TutorialInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref TutorialInstanceRole
#
# Log Groups
#
TutorialBootLog:
Type: AWS::Logs::LogGroup
TutorialKernelLog:
Type: AWS::Logs::LogGroup
#
# Instances
#
TutorialDatabaseServer:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/profile.d/cloudformation-init.sh:
content: !Sub |
export BACKUP_S3_BUCKET="${TutorialBackupS3Bucket}"
/etc/amazon/amazon-cloudwatch-agent.json:
content: !Sub |
{
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${!aws:AutoScalingGroupName}",
"ImageId": "${!aws:ImageId}",
"InstanceId": "${!aws:InstanceId}",
"InstanceType": "${!aws:InstanceType}"
},
"metrics_collected": {
"cpu": {
"measurement": [
"cpu_usage_idle",
"cpu_usage_iowait",
"cpu_usage_user",
"cpu_usage_system"
],
"metrics_collection_interval": 30,
"totalcpu": false
},
"disk": {
"measurement": [
"used_percent",
"inodes_free"
],
"metrics_collection_interval": 30,
"resources": [
"*"
]
},
"diskio": {
"measurement": [
"io_time"
],
"metrics_collection_interval": 30,
"resources": [
"*"
]
},
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 30
},
"statsd": {
"metrics_aggregation_interval": 30,
"metrics_collection_interval": 10,
"service_address": ":8125"
},
"swap": {
"measurement": [
"swap_used_percent"
],
"metrics_collection_interval": 30
}
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"log_group_name": "${TutorialBootLog}",
"file_path": "/var/log/boot.log"
},
{
"log_group_name": "${TutorialKernelLog}",
"file_path": "/var/log/messages"
}
]
}
}
}
}
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
KeyName: !Ref KeyPairName
NetworkInterfaces:
- SubnetId: !Ref TutorialPrivateSubnet
DeviceIndex: 0
GroupSet:
- !Ref TutorialPrivateSecurityGroup
BlockDeviceMappings:
- DeviceName: !Sub "/dev/${RootDevice}"
Ebs:
VolumeSize: !Ref VolumeSize
VolumeType: gp2
IamInstanceProfile: !Ref TutorialInstanceProfile
Tags:
- Key: "Name"
Value: "Tutorial Database Server"
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
# cloudformation initialize
/opt/aws/bin/cfn-init -v -s ${AWS::StackName} --region ${AWS::Region} -r TutorialDatabaseServer
# download and install cloudwatch agent
wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
yum -y install amazon-cloudwatch-agent.rpm
mv /etc/amazon/amazon-cloudwatch-agent.json /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.json
sudo systemctl enable amazon-cloudwatch-agent
sudo systemctl start amazon-cloudwatch-agent
TutorialWebServer:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
/etc/profile.d/cloudformation-init.sh:
content: !Sub |
export BACKUP_S3_BUCKET="${TutorialBackupS3Bucket}"
export DATABASE_SERVER="${TutorialDatabaseServer.PrivateIp}"
/etc/amazon/amazon-cloudwatch-agent.json:
content: !Sub |
{
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${!aws:AutoScalingGroupName}",
"ImageId": "${!aws:ImageId}",
"InstanceId": "${!aws:InstanceId}",
"InstanceType": "${!aws:InstanceType}"
},
"metrics_collected": {
"cpu": {
"measurement": [
"cpu_usage_idle",
"cpu_usage_iowait",
"cpu_usage_user",
"cpu_usage_system"
],
"metrics_collection_interval": 30,
"totalcpu": false
},
"disk": {
"measurement": [
"used_percent",
"inodes_free"
],
"metrics_collection_interval": 30,
"resources": [
"*"
]
},
"diskio": {
"measurement": [
"io_time"
],
"metrics_collection_interval": 30,
"resources": [
"*"
]
},
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 30
},
"statsd": {
"metrics_aggregation_interval": 30,
"metrics_collection_interval": 10,
"service_address": ":8125"
},
"swap": {
"measurement": [
"swap_used_percent"
],
"metrics_collection_interval": 30
}
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"log_group_name": "${TutorialBootLog}",
"file_path": "/var/log/boot.log"
},
{
"log_group_name": "${TutorialKernelLog}",
"file_path": "/var/log/messages"
}
]
}
}
}
}
Properties:
ImageId: !Ref ImageId
InstanceType: !Ref InstanceType
KeyName: !Ref KeyPairName
NetworkInterfaces:
- SubnetId: !Ref TutorialPublicSubnet
DeviceIndex: 0
GroupSet:
- !Ref TutorialPublicSecurityGroup
BlockDeviceMappings:
- DeviceName: !Sub "/dev/${RootDevice}"
Ebs:
VolumeSize: !Ref VolumeSize
VolumeType: gp2
IamInstanceProfile: !Ref TutorialInstanceProfile
Tags:
- Key: "Name"
Value: "Tutorial Web Server"
UserData:
Fn::Base64:
!Sub |
#!/bin/bash -xe
# cloudformation initialize
/opt/aws/bin/cfn-init -v -s ${AWS::StackName} --region ${AWS::Region} -r TutorialWebServer
# download and install cloudwatch agent
wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
yum -y install amazon-cloudwatch-agent.rpm
mv /etc/amazon/amazon-cloudwatch-agent.json /etc/amazon/amazon-cloudwatch-agent/amazon-cloudwatch-agent.json
sudo systemctl enable amazon-cloudwatch-agent
sudo systemctl start amazon-cloudwatch-agent
#
# Topic for monitoring alarms
#
# Subscriptions may be created through the SNS console (i.e. email, SMS, etc.)
#
TutorialAlarmTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: TutorialAlarms
#
# Alarms
#
TutorialDatabaseCPUAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Database Server CPU Usage High
AlarmActions:
- !Ref TutorialAlarmTopic
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: 60
EvaluationPeriods: 3
Threshold: 89
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: InstanceId
Value: !Ref TutorialDatabaseServer
TutorialDatabaseDiskAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Database Server Disk Usage High
AlarmActions:
- !Ref TutorialAlarmTopic
MetricName: disk_used_percent
Namespace: CWAgent
Statistic: Average
Period: 60
EvaluationPeriods: 3
Threshold: 89
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: InstanceId
Value: !Ref TutorialDatabaseServer
- Name: ImageId
Value: !Ref ImageId
- Name: InstanceType
Value: !Ref InstanceType
- Name: path
Value: /
- Name: device
Value: !Sub ${RootDevice}1
- Name: fstype
Value: !Ref RootFsType
TutorialWebCPUAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Database Server CPU Usage High
AlarmActions:
- !Ref TutorialAlarmTopic
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: 60
EvaluationPeriods: 3
Threshold: 89
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: InstanceId
Value: !Ref TutorialWebServer
TutorialWebDiskAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Web Server Disk Usage High
AlarmActions:
- !Ref TutorialAlarmTopic
MetricName: disk_used_percent
Namespace: CWAgent
Statistic: Average
Period: 60
EvaluationPeriods: 3
Threshold: 89
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: InstanceId
Value: !Ref TutorialWebServer
- Name: ImageId
Value: !Ref ImageId
- Name: InstanceType
Value: !Ref InstanceType
- Name: path
Value: /
- Name: device
Value: !Sub ${RootDevice}1
- Name: fstype
Value: !Ref RootFsType
#
# Backups
#
TutorialLambdaBackupRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: NFormLambdasBackupRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:CreateSnapshot
- ec2:DeleteSnapshot
- ec2:Describe*
- ec2:ModifySnapshotAttribute
- ec2:ResetSnapshotAttribute
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
- xray:GetSamplingStatisticSummaries
- logs:*
Resource: "*"
#
# Backup Lambda with code for the job
#
TutorialCreateBackupLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: ebs-snapshots-create
Description: Create EBS Snapshots
Handler: index.handler
Role: !GetAtt TutorialLambdaBackupRole.Arn
Environment:
Variables:
REGIONS: !Ref AWS::Region
Runtime: python3.6
Timeout: 90
TracingConfig:
Mode: Active
Code:
ZipFile: |
import os
from datetime import datetime, timedelta
import boto3
def handler(event, context):
ec2_client = boto3.client("ec2")
env_regions = os.getenv("REGIONS", None)
clientTag = os.getenv("CLIENT_BACKUP_TAG")
projectTag = os.getenv("PROJECT_BACKUP_TAG")
today = datetime.today()
total_created = 0
if env_regions:
regions = ec2_client.describe_regions(RegionNames = env_regions.split(","))
else:
return total_created
# loop through all of our regions
for region in regions.get("Regions", []):
regionName = region["RegionName"]
print("Checking for volumes in region %s" % (regionName))
ec2_client = boto3.client("ec2", region_name = region["RegionName"])
# query for volumes with matching tags that are in use
result = ec2_client.describe_volumes(Filters = [
{"Name": "status", "Values": ["in-use"]}
])
if not result:
print("No matching EBS volumes with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_created
for volume in result["Volumes"]:
volume_name = volume["VolumeId"]
volume_description = "Created by ebs-snapshots-create"
# check the volume for matching tags
if "Tags" in volume:
for tag in volume["Tags"]:
if tag["Key"] == "Name":
volume_name = tag["Value"]
elif tag["Key"] == "Description":
volume_description = tag["Value"]
# daily
print("Creating snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Daily"}
])
total_created += 1
# quarterly
if today.day == 1 and today.month % 4 == 0:
print("Creating quarterly snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Quarterly"}
])
total_created += 1
# annual
if today.day == 31 and today.month == 12:
print("Creating annual snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Annual"},
])
total_created += 1
return total_created
#
# Delete old backups, enforce retention policy
TutorialDeleteBackupLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: ebs-snapshots-delete
Description: Delete Old EBS Snapshots
Handler: index.handler
Role: !GetAtt TutorialLambdaBackupRole.Arn
Environment:
Variables:
REGIONS: !Ref AWS::Region
Runtime: python3.6
Timeout: 90
TracingConfig:
Mode: Active
Code:
ZipFile: |
import os
from datetime import datetime, timedelta
from dateutil import relativedelta
import boto3
def handler(event, context):
ec2_client = boto3.client("ec2")
date_format = "%Y-%m-%dT%H:%M:%S.%fZ"
daily_cutoff = (datetime.utcnow() - timedelta(days = 7)).strftime(date_format)
env_regions = os.getenv("REGIONS", None)
today = datetime.today()
total_deleted = 0
if env_regions:
regions = ec2_client.describe_regions(RegionNames = env_regions.split(","))
else:
return total_created
for region in regions.get("Regions", []):
regionName = region["RegionName"]
print("Checking for snapshots in region %s" % (regionName))
# query for Daily snapshots with matching tags
result = ec2_client.describe_snapshots(Filters = [
{"Name": "tag:Interval", "Values": ["Daily"]}
])
if not result:
print("No matching Daily snapshots with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_deleted
for snapshot in result["Snapshots"]:
snapshot_time = snapshot["StartTime"].strftime(date_format)
snapshot_id = snapshot["SnapshotId"]
if daily_cutoff > snapshot_time:
snapshot_name = ""
if "Tags" in snapshot:
for tag in snapshot["Tags"]:
if tag["Key"] == "Name":
snapshot_name = tag["Value"]
print("Deleting Daily EBS snapshot ID = %s, Name = %s" % (snapshot_id, snapshot_name))
ec2_client.delete_snapshot(SnapshotId = snapshot_id)
total_deleted += 1
# query for quarterly snapshots with matching tags
if today.day == 1 and today.month % 4 == 1:
quarterly_cutoff_raw = datetime.utcnow() - relativedelta.relativedelta(months = 15) # keep five quarters
quarterly_cutoff = quarterly_cutoff_raw.strftime(date_format)
result = ec2_client.describe_snapshots(Filters = [
{"Name": "tag:Interval", "Values": ["Quarterly"]}
])
if not result:
print("No matching Quarterly snapshots with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_deleted
for snapshot in result["Snapshots"]:
snapshot_time = snapshot["StartTime"].strftime(date_format)
snapshot_id = snapshot["SnapshotId"]
if quarterly_cutoff > snapshot_time:
snapshot_name = ""
if "Tags" in snapshot:
for tag in snapshot["Tags"]:
if tag["Key"] == "Name":
snapshot_name = tag["Value"]
print("Deleting Quarterly EBS snapshot ID = %s, Name = %s" % (snapshot_id, snapshot_name))
ec2_client.delete_snapshot(SnapshotId = snapshot_id)
total_deleted += 1
return total_deleted
#
# Rules to trigger our backup and retention jobs
#
TutorialPerformBackupRule:
Type: AWS::Events::Rule
Properties:
Description: Triggers our backup lambda script
ScheduleExpression: cron(30 4 * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt TutorialCreateBackupLambda.Arn
Id: TutorialBackupRule
TutorialDeleteBackupRule:
Type: AWS::Events::Rule
Properties:
Description: Triggers our backup delete lambda script
ScheduleExpression: cron(45 4 * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt TutorialDeleteBackupLambda.Arn
Id: TutorialBackupRule
#
# Set permissions for our backup and retention jobs
#
TutorialPermissionCreate:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt TutorialCreateBackupLambda.Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt TutorialPerformBackupRule.Arn
TutorialPermissionDelete:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt TutorialDeleteBackupLambda.Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt TutorialDeleteBackupRule.Arn
Outputs:
WebServer:
Description: A reference to the web server
Value: !Ref TutorialWebServer
DatabaseServer:
Description: A reference to the database server
Value: !Ref TutorialDatabaseServer
BackupS3Bucket:
Description: S3 Bucket used to backup data
Value: !Sub |
https://${TutorialBackupS3Bucket.DomainName}