in resource-compliance-lambda/handler.py [0:0]
def lambda_handler(event, context):
# get account id
account_id = context.invoked_function_arn.split(":")[4]
# Need to make sure we don't waste time if the request type is
# update or delete. Exit gracefully
if event['RequestType'] == "Delete":
logger.info(f'Request Type is Delete; unsupported')
cfnsend(event, context, 'SUCCESS', responsedata)
return event
if event['RequestType'] == "Update":
logger.info(f'Request Type is Update; unsupported')
cfnsend(event, context, 'SUCCESS', responsedata)
return event
# check if action provided in request
if 'Action' in event['ResourceProperties']:
action = (event['ResourceProperties']['Action']['Name'] if 'Name' in event['ResourceProperties']['Action']
else None)
params = (event['ResourceProperties']['Action']['Parameters'] if action and 'Parameters' in event['ResourceProperties']['Action']
else None)
# Convert tags provided as string in CFN to JSON format
if 'json' == action.lower() and params:
jObject = []
keyList=[]
sqsTags={}
# Read tag string from paramter
tags = (params['JSON'] if 'JSON' in params else '')
type = (params['Type'] if 'Type' in params else None)
logger.info(f'Processing tags: {tags}')
try:
# Convert string to JSON object
for t in tags.split(','):
tg=None
k = t.split('=')
if len(k) != 2:
logger.info(f'No Key Value found in: '+t)
continue
if type.lower() == 'tags':
tg = {"Key":k[0], "Value":k[1]}
keyList.append(k[0])
elif type.lower() == 'dynamodbschema':
tg = {"AttributeName":k[0], "AttributeType":k[1]}
elif type.lower() == 'dynamodbkey':
tg = {"AttributeName":k[0], "KeyType":k[1]}
elif type.lower() == 'sqs':
sqsTags[k[0]] = k[1]
if tg:
jObject.append(tg)
except Exception as e:
logger.info(f'Error processing tags: {str(e)}')
responsedata['error'] = str(e)
cfnsend(event, context, 'FAILED', responsedata,'Error processing tags')
return event
# Validate if all require tags were provided
if type.lower() == 'tags':
for t in requireTags:
if not t in keyList:
# Failed CFN stack if missing require tag
logger.info(f'Missing require tag: {t}')
cfnsend(event, context, 'FAILED', responsedata,'Missing require tag: '+t)
return event
# Add tags to SQS
if type.lower() == 'sqs':
queueURL = (params['SQS'] if 'SQS' in params else None)
# Check if SQS URL provided
if not queueURL:
# Failed CFN stack if missing SQS URL
logger.info(f'Missing SQS URI')
cfnsend(event, context, 'FAILED', responsedata,'Missing SQS URI')
return event
# check if tags provided
if not sqsTags:
# Failed CFN stack if missing tags
logger.info(f'Missing SQS tags')
cfnsend(event, context, 'FAILED', responsedata,'Missing SQS tags')
return event
#add tags to sqs
try:
logger.info(f'Add Tags to SQS: {queueURL}')
response = sqs.tag_queue(
QueueUrl=queueURL,
Tags=sqsTags
)
except Exception as e:
logger.info(f'Error adding tag to SQS: {str(e)}')
responsedata['error'] = str(e)
cfnsend(event, context, 'FAILED', responsedata,'Error adding tags to SQS')
return event
# response formated JSON object back to CFN
responsedata['Json'] = jObject
# put S3 notification to trigger lambda when new object created
if 's3notification' == action.lower() and params:
bucket_name = (params['Bucket'] if 'Bucket' in params else None)
lambda_arn = (params['Lambda'] if 'Lambda' in params else None)
filterRules = (params['FilterRules'] if 'FilterRules' in params else None)
if bucket_name and lambda_arn:
try:
response = lmd.add_permission(
Action='lambda:InvokeFunction',
FunctionName=lambda_arn,
Principal='s3.amazonaws.com',
SourceArn='arn:aws:s3:::'+bucket_name,
SourceAccount=account_id,
StatementId=str(uuid.uuid4())
)
bucket_notification = s3.BucketNotification(bucket_name)
if not filterRules:
response = bucket_notification.put(
NotificationConfiguration={
'LambdaFunctionConfigurations': [
{
'LambdaFunctionArn': lambda_arn,
'Events': [
's3:ObjectCreated:*'
]
}
]
}
)
else:
response = bucket_notification.put(
NotificationConfiguration={
'LambdaFunctionConfigurations': [
{
'LambdaFunctionArn': lambda_arn,
'Events': [
's3:ObjectCreated:*'
],
'Filter': {
'Key': {
'FilterRules': filterRules
}
}
}
]
}
)
responsedata['Status']='SUCCESS'
except Exception as e:
logger.info(f'Error processing S3 Notification: {str(e)}')
responsedata['error'] = str(e)
cfnsend(event, context, 'FAILED', responsedata,'Error processing S# Notification')
return event
else:
cfnsend(event, context, 'FAILED', responsedata,'S3 Notification - missing Bucket or Lambda paramters')
return event
# validate if kms is BYOK
if 'byok' == action.lower() and params:
key = (params['Key'] if 'Key' in params else None)
# check if mks key provided
if key:
try:
# get information about kms key
response = kms.describe_key(
KeyId=key
)
# check if kms key is BYOK
if response['KeyMetadata']['Origin'] != 'EXTERNAL':
logger.info(f'BYOK No Found: {key}')
responsedata['Id'] = 'Provided Key is not BYOK'
cfnsend(event, context, 'FAILED', responsedata,'Provided Encryption Key is not BYOK')
return event
except Exception as e:
logger.info(f'Error processing KMS: {str(e)}')
responsedata['error'] = str(e)
cfnsend(event, context, 'FAILED', responsedata,'Error processing KMS')
return event
# Failed CFN stack if key not provided
else:
cfnsend(event, context, 'FAILED', responsedata,'No Encryption Key Provided')
return event
# validate if principal are not publicly open
if 'principal' == action.lower() and params:
accountid = (params['Account'] if 'Account' in params else None)
principals = ((params['Principal']).split(',') if 'Principal' in params else None)
type = (params['Type'] if 'Type' in params else None)
if not accountid or not principals:
# Failed CFN stack if account id or principal missing
logger.info(f'Missing account id and/or principal')
cfnsend(event, context, 'FAILED', responsedata,'Missing account id and/or principal')
return event
npList = []
npstr = None
if not type:
for p in principals:
np = p
if p == "*":
np = accountid
if not np in npList:
npList.append(np)
npstr = ','.join(npList)
elif type == 'kms':
for p in principals:
np = p.replace('{accountid}',accountid)
npList.append(np)
npstr = npList
# return formated principal back to CFN
responsedata['Principal'] = npstr
# Using the cfnsend function to format our response to Cloudforamtion and send it
cfnsend(event, context, 'SUCCESS', responsedata)
return event