Adam Pippin
4 years ago
commit
ea0e810c4b
2 changed files with 202 additions and 0 deletions
@ -0,0 +1,166 @@ |
|||||
|
import json |
||||
|
import boto3 |
||||
|
import time |
||||
|
|
||||
|
# The account id sharing snapshots with you |
||||
|
SOURCE_ACCOUNT = "987698769876" |
||||
|
# The account id this is running in |
||||
|
TARGET_ACCOUNT = "123412341234" |
||||
|
# Mapping of KMS keys -- keys are the key arn in the source account, values |
||||
|
# are the key arn in this account. |
||||
|
KMS_KEYS = { |
||||
|
"arn:aws:kms:ca-central-1:987698769876:key/abcd-abcd-abcd": "arn:aws:kms:ca-central-1:123412341234:key/qrst-qrst-qrst" |
||||
|
} |
||||
|
# Configure retention period for deleted snapshots. If the string is present |
||||
|
# in the snapshot description, then the retention time will be the number of |
||||
|
# seconds given in the value. If multiple strings match, which one is chosen |
||||
|
# is undefined. |
||||
|
RETENTION = { |
||||
|
"hour_snapshot": 0, |
||||
|
"day_snapshot": 86400, |
||||
|
"week_snapshot": 259200, |
||||
|
"month_snapshot": 259200, |
||||
|
"year_snapshot": 259200 |
||||
|
} |
||||
|
# Name of the tag on the snapshots to store the source snapshot id in. |
||||
|
SOURCE_TAG = "SourceSnapshotId" |
||||
|
# Name of the tag on the snapshots to store the retention period in. |
||||
|
RETENTION_TAG = "RetentionPeriod" |
||||
|
# Region the source snapshots are in. |
||||
|
REGION = "ca-central-1" |
||||
|
|
||||
|
print("START") |
||||
|
ec2 = boto3.client('ec2') |
||||
|
|
||||
|
def lambda_handler(event, context): |
||||
|
print("Fetching snapshots for source account " + SOURCE_ACCOUNT) |
||||
|
source_snapshots = fetch_snapshots(SOURCE_ACCOUNT) |
||||
|
print("Fetching snapshots for target account: " + TARGET_ACCOUNT) |
||||
|
target_snapshots = fetch_snapshots(TARGET_ACCOUNT) |
||||
|
source_snapshot_index = {} |
||||
|
target_snapshot_index = {} |
||||
|
target_snapshots_mapped = {} |
||||
|
target_snapshots_orphaned = {} |
||||
|
|
||||
|
results = [] |
||||
|
|
||||
|
for snapshot in source_snapshots: |
||||
|
source_snapshot_index[snapshot['SnapshotId']] = snapshot |
||||
|
|
||||
|
for snapshot in target_snapshots: |
||||
|
target_snapshot_index[snapshot['SnapshotId']] = snapshot |
||||
|
|
||||
|
for tag in snapshot['Tags']: |
||||
|
if tag['Key'] == SOURCE_TAG: |
||||
|
source_snapshot_id = tag['Value'] |
||||
|
if source_snapshot_id in source_snapshot_index.keys(): |
||||
|
target_snapshots_mapped[source_snapshot_id] = snapshot |
||||
|
else: |
||||
|
target_snapshots_orphaned[snapshot['SnapshotId']] = snapshot |
||||
|
|
||||
|
for source_snapshot_id, source_snapshot in source_snapshot_index.items(): |
||||
|
if source_snapshot_id in target_snapshots_mapped.keys(): |
||||
|
results.append(handle_snapshot(source_snapshot, target_snapshots_mapped[source_snapshot_id])) |
||||
|
else: |
||||
|
results.append(handle_snapshot(source_snapshot, None)) |
||||
|
|
||||
|
for target_snapshot_id in target_snapshots_orphaned: |
||||
|
results.append(handle_snapshot(None, target_snapshots_orphaned[target_snapshot_id])) |
||||
|
|
||||
|
results = list(filter(None, results)) |
||||
|
|
||||
|
return { |
||||
|
'statusCode': 200, |
||||
|
'body': json.dumps(results) |
||||
|
} |
||||
|
|
||||
|
def fetch_snapshots(account_id): |
||||
|
return ec2.describe_snapshots( |
||||
|
Filters = [ |
||||
|
{ |
||||
|
'Name': 'owner-id', |
||||
|
'Values': [ account_id ] |
||||
|
} |
||||
|
] |
||||
|
)['Snapshots'] |
||||
|
|
||||
|
def handle_snapshot(source_snapshot, target_snapshot): |
||||
|
if source_snapshot is not None and target_snapshot is not None: |
||||
|
# Source and target exist, nothing to do! |
||||
|
pass |
||||
|
elif source_snapshot is not None and target_snapshot is None: |
||||
|
# Source exists, no target. Copy! |
||||
|
print(source_snapshot['SnapshotId'] + " copying") |
||||
|
|
||||
|
# Quick guard -- make sure the snapshot is complete first. |
||||
|
if source_snapshot['Progress'] != '100%': |
||||
|
return [source_snapshot['SnapshotId'], None, "Source snapshot incomplete. Doing nothing."] |
||||
|
|
||||
|
copy_response = ec2.copy_snapshot( |
||||
|
Description = '/'.join([ |
||||
|
source_snapshot['OwnerId'], |
||||
|
source_snapshot['VolumeId'], |
||||
|
str(source_snapshot['StartTime']), |
||||
|
source_snapshot['Description'] |
||||
|
]), |
||||
|
Encrypted = True, |
||||
|
KmsKeyId = KMS_KEYS[source_snapshot['KmsKeyId']], |
||||
|
SourceRegion = REGION, |
||||
|
SourceSnapshotId = source_snapshot['SnapshotId'], |
||||
|
TagSpecifications = [ |
||||
|
{ |
||||
|
'ResourceType': 'snapshot', |
||||
|
'Tags': [ |
||||
|
{ |
||||
|
"Key": SOURCE_TAG, |
||||
|
"Value": source_snapshot['SnapshotId'] |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
] |
||||
|
) |
||||
|
|
||||
|
return [source_snapshot['SnapshotId'], copy_response['SnapshotId'], "Copied snapshot"] |
||||
|
|
||||
|
elif source_snapshot is None and target_snapshot is not None: |
||||
|
# Check if there's already a retention tag |
||||
|
|
||||
|
retention = None |
||||
|
|
||||
|
for tag in target_snapshot['Tags']: |
||||
|
if tag['Key'] == RETENTION_TAG: |
||||
|
retention = float(tag['Value']) |
||||
|
break |
||||
|
|
||||
|
if retention is None: |
||||
|
# Mark target with retention tag |
||||
|
print(target_snapshot['SnapshotId'] + ": marked for removal") |
||||
|
|
||||
|
retention = time.time() |
||||
|
description = target_snapshot['Description'] |
||||
|
for search_str, retention_seconds in RETENTION.items(): |
||||
|
if search_str in description: |
||||
|
retention = retention + retention_seconds |
||||
|
break |
||||
|
|
||||
|
ec2.create_tags( |
||||
|
Resources=[ |
||||
|
target_snapshot['SnapshotId'] |
||||
|
], |
||||
|
Tags=[ |
||||
|
{ |
||||
|
'Key': RETENTION_TAG, |
||||
|
'Value':str(retention) |
||||
|
} |
||||
|
] |
||||
|
) |
||||
|
|
||||
|
return [None, target_snapshot['SnapshotId'], "Source removed. Marking for deletion."] |
||||
|
|
||||
|
elif retention < time.time(): |
||||
|
print(target_snapshot['SnapshotId'] + ": retention time passed, removing") |
||||
|
ec2.delete_snapshot( |
||||
|
SnapshotId = target_snapshot['SnapshotId'] |
||||
|
) |
||||
|
return [None, target_snapshot['SnapshotId'], "Retention period passed. Deleted."] |
||||
|
|
@ -0,0 +1,36 @@ |
|||||
|
import json |
||||
|
import boto3 |
||||
|
|
||||
|
VOLUME_ID = 'vol-123412341234' |
||||
|
TARGET_ACCOUNT = "123412341234" |
||||
|
|
||||
|
print("START") |
||||
|
|
||||
|
def lambda_handler(event, context): |
||||
|
ec2 = boto3.resource('ec2') |
||||
|
print("Fetching snapshots for volume " + VOLUME_ID) |
||||
|
volume = ec2.Volume(id=VOLUME_ID) |
||||
|
snapshots = volume.snapshots.all() |
||||
|
snapshot_ids = [] |
||||
|
|
||||
|
for snapshot in snapshots: |
||||
|
shared = snapshot.describe_attribute(Attribute='createVolumePermission') |
||||
|
already_shared = False |
||||
|
|
||||
|
for i in range(0, len(shared['CreateVolumePermissions'])): |
||||
|
if 'UserId' in shared['CreateVolumePermissions'][i] and shared['CreateVolumePermissions'][i]['UserId'] == TARGET_ACCOUNT: |
||||
|
already_shared = True |
||||
|
break |
||||
|
|
||||
|
if not already_shared: |
||||
|
print("Sharing " + snapshot.id) |
||||
|
snapshot_ids.append(snapshot.id) |
||||
|
snapshot.modify_attribute( |
||||
|
Attribute='createVolumePermission', |
||||
|
OperationType='add', |
||||
|
UserIds=[TARGET_ACCOUNT]) |
||||
|
|
||||
|
return { |
||||
|
'statusCode': 200, |
||||
|
'body': json.dumps(snapshot_ids) |
||||
|
} |
Loading…
Reference in new issue