-
Notifications
You must be signed in to change notification settings - Fork 7
/
rdscopysnapshots-lambda.py
127 lines (112 loc) · 4.33 KB
/
rdscopysnapshots-lambda.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
from __future__ import print_function
from datetime import tzinfo, timedelta, datetime
import time
from boto3 import client
import json
# Lambda function that makes a manual copy of the most recent
# auto snapshot for one or more RDS instances, shares it with a
# 'restricted' Failsafe account, sends an SNS notification
# and then tidies up after itself.
# List of database identifiers
INSTANCES = ["db-name1", "db-name2"]
# AWS region in which the db instances exist
REGION = "eu-west-1"
# The account to share Failsafe snapshots with
SHAREWITH = "012345678901"
# SNS topic ARN to announce availability of the manual snapshot copy
SNSARN = "arn:aws:sns:eu-west-1:012345678901:rds-copy-snapshots"
# Handle timezones correctly
ZERO = timedelta(0)
class UTC(tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
utc = UTC()
def create_manual_copy(rds, instance):
print("Creating manual copy of the most recent auto snapshot of {}".format(instance))
autos = get_snaps(rds, instance, 'automated')
newest = autos[-1]
newestname = newest['DBSnapshotIdentifier']
failsafename = "failsafe-"+newest['DBSnapshotIdentifier'][4:]
manualexists = False
manuals = get_snaps(rds, instance, 'manual')
for manual in manuals:
if manual['DBSnapshotIdentifier'] == failsafename:
print("Manual snapshot already exists for auto snapshot {}".format(newestname))
return
rds.copy_db_snapshot(
SourceDBSnapshotIdentifier=newestname,
TargetDBSnapshotIdentifier=failsafename
)
wait_until_available(rds, instance, failsafename)
print("Snapshot {} copied to {}".format(newestname, failsafename))
share_snapshot(rds, failsafename)
send_sns(instance, failsafename)
def send_sns(instance, failsafename):
if SNSARN:
print("Sending SNS alert")
message = {"Instance": instance, "FailsafeSnapshotID": failsafename}
sns = client("sns", region_name=REGION)
response = sns.publish(
TargetArn=SNSARN,
Message=json.dumps({'default': json.dumps(message)}),
MessageStructure='json'
)
def share_snapshot(rds, failsafename):
if SHAREWITH:
print("Sharing {}".format(failsafename))
rds.modify_db_snapshot_attribute(
DBSnapshotIdentifier=failsafename,
AttributeName='restore',
ValuesToAdd=[
SHAREWITH
]
)
def wait_until_available(rds, instance, snapshot):
print("Waiting for copy of {} to complete.".format(snapshot))
available = False
while not available:
time.sleep(10)
manuals = get_snaps(rds, instance, 'manual')
for manual in manuals:
if manual['DBSnapshotIdentifier'] == snapshot:
#print("{}: {}...".format(manual['DBSnapshotIdentifier'], manual['Status']))
if manual['Status'] == "available":
available = True
break
def delete_old_manuals(rds, instance):
print("Deleting old manual snapshots for {}".format(instance))
manuals = get_snaps(rds, instance, 'manual')
for manual in manuals:
# Only check Failsafe manual snapshots
if manual['DBSnapshotIdentifier'][:9] != "failsafe-":
print("Ignoring {}".format(manual['DBSnapshotIdentifier']))
continue
print("Deleting {}".format(manual['DBSnapshotIdentifier']))
rds.delete_db_snapshot(
DBSnapshotIdentifier=manual['DBSnapshotIdentifier']
)
def get_snap_date(snap):
# If snapshot is still being created it doesn't have a SnapshotCreateTime
if snap['Status'] != "available":
return datetime.now(utc)
else:
return snap['SnapshotCreateTime']
def get_snaps(rds, instance, snap_type):
snapshots = rds.describe_db_snapshots(
SnapshotType=snap_type,
DBInstanceIdentifier=instance)['DBSnapshots']
if len(snapshots) > 0:
snapshots = sorted(snapshots, key=get_snap_date)
return snapshots
def handler(event, context):
rds = client("rds", region_name=REGION)
if INSTANCES:
for instance in INSTANCES:
delete_old_manuals(rds, instance)
create_manual_copy(rds, instance)
else:
print("You must populate the INSTANCES variable.")