I have a lambda which i want to run every morning showing the users with AccessKeys older than 90 days. What i am noticing is that the return of users is limited
import boto3, json, time, datetime, sys, pprint
sns = boto3.client('sns')
usernames = []
mylist = []
sts_client = boto3.client('sts')
assumed_role_object=sts_client.assume_role(
RoleArn="arn:aws:iam::11111111:role/lambda_role",
RoleSessionName="AssumedRoleSession4"
)
credentials=assumed_role_object['Credentials']
client=boto3.client(
'iam',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
def lambda_handler(event, context):
users = client.list_users()
for key in users['Users']:
a = str(key['UserName'])
usernames.append(a)
for username in usernames:
try:
res = client.list_access_keys(UserName=username)
accesskeydate = res['AccessKeyMetadata'][0]['CreateDate']
accesskeydate = accesskeydate.strftime("%Y-%m-%d %H:%M:%S")
currentdate = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
accesskeyd = time.mktime(datetime.datetime.strptime(accesskeydate, "%Y-%m-%d %H:%M:%S").timetuple())
currentd = time.mktime(datetime.datetime.strptime(currentdate, "%Y-%m-%d %H:%M:%S").timetuple())
active_days = (currentd - accesskeyd)/60/60/24 ### We get the data in seconds. converting it to days
if 90 < active_days:
a = str(username)
c = int(int(round(active_days)))
mylist.append(a)
mylist.append(c)
except:
f = str('')
print(mylist)
finallist = ''.join(str(mylist))
finallist = finallist
sns_message = (finallist)
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:111111:sns',
Message= sns_message,
Subject='Access Keys which need rotating',
)
How do i ensure all the users are captured in the python query and then emailed out via SNS?
Thanks
If you read the IAM list_users() documentation, you will see that results can be truncated and you need to paginate. The response you get back will include IsTruncated=True in which case you need to re-issue the list_users() call, supplying Marker, which you retrieve from the previous response.
Alternatively, and probably preferably, you can use a built-in paginator.
Related
I'm a new learner for AWS Lambda.
I'm trying to get data from AWS Cost Explorer and send a slack daily.
The data should be descending order but it's not.
Can you please give some tips for my code?
AWS Lambda seems not to allow to use .getValue() for sort function.
def lambda_handler(event, context):
client = boto3.client('ce')
#get cost for each service daily
serviceCost = get_daily_serviceCost(client)
(title, detail) = create_message(totalCost, serviceCost)
#transfer message to slack
post_slack(title, detail)
def get_daily_serviceCost(client) -> list:
today = datetime.date.today()
yesterday = datetime.date.today() - datetime.timedelta(days=1)
price = client.get_cost_and_usage(
TimePeriod={
'Start':datetime.date.strftime(yesterday, '%Y-%m-%d'),
'End':datetime.date.strftime(today, '%Y-%m-%d')
},
Granularity='DAILY',
Metrics=['BlendedCost'],
GroupBy=[
{
'Type':'DIMENSION',
'Key':'SERVICE'
}
]
)
billings = []
for item in price['ResultsByTime'][0]['Groups']:
billings.append({
'service_name':item['Keys'][0],
'billing':item['Metrics']['BlendedCost']['Amount']
})
return billings
def create_message(serviceCost:list) -> (str):
yesterday = datetime.date.today() - datetime.timedelta(days=1)
details = []
for item in serviceCost:
service_name = item['service_name']
billing = round(float(item['billing']),2)
if billing == 0.00:
continue
details.append({service_name, billing})
for check in details:
print(check)
test = []
for what in serviceCost:
test.append({service_name, billing})
# print(what)
test.sort(key=lambda k: k[1], reverse=True)
I am struggling to convert this captured output as a dictionary so i can distribute the list of users who have a password over 90 days old:
import boto3
import datetime
from dateutil.tz import tzutc
sts_client = boto3.client('sts')
assumed_role_object=sts_client.assume_role(
RoleArn="arn:aws:iam::9999999999:role/role",
RoleSessionName="AssumedRoleSession2"
)
credentials=assumed_role_object['Credentials']
resource=boto3.resource(
'iam',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
)
sns = boto3.client('sns')
import json
import boto3
def lambda_handler(context,event):
today = datetime.datetime.now()
for user in resource.users.all():
if user.password_last_used is not None:
delta = (today - user.password_last_used.replace(tzinfo=None)).days
if delta >= 90:
print(','.join(('Username: ',str([user.user_name]), str(delta))))
sns_message = ','.join(('Username: ',str([user.user_name]), str(delta)))
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:1111111111:topic',
Message= sns_message,
Subject='Users who have failed to log in over 90 Days',
)
My current attempt is causing 189 emails containing a single user name and password age, i want an email contenting a list of users and their password ages
Is there anyone kind enough to help me?
Thanks
Nick
Your sns.publish is inside your for loop. Create your list of users inside the loop then call the publish command.
I may get some of the syntax wrong as i can't test, but hopefully you'll the point.
user_list = []
for user in resource.users.all():
if user.password_last_used is not None:
delta = (today - user.password_last_used.replace(tzinfo=None)).days
if delta >= 90:
print(','.join(('Username: ',str([user.user_name]), str(delta))))
user_list.append(','.join(('Username: ',str([user.user_name]), str(delta))))
sns_message = user_list # you may need to check formatting of this prior to sending to sns???? it's a list, not a string
response = sns.publish(
TopicArn='arn:aws:sns:eu-west-2:1111111111:topic',
Message= sns_message,
Subject='Users who have failed to log in over 90 Days',
)
I didn't get an answer to my original question, but I've tweaked my code and I'm leaving this here in case anyone is trying to figure this out in the future. I'm new to Python, but this worked. This is a Python script I created for a Lambda function that checks the users in your AWS account and publishes a notification to an SNS topic. I've scheduled a CloudWatch rule with a cron expression to run it every day.
import boto3, json, time, datetime, sys, re
iam_client = boto3.client('iam')
sns_client = boto3.client('sns')
users = iam_client.list_users()
user_list = []
for key in users['Users']:
user_list = key['UserName']
accesskeys = iam_client.list_access_keys(UserName=key['UserName'])
for items in user_list.split('\n'):
for key in accesskeys['AccessKeyMetadata']:
accesskeydate = accesskeys['AccessKeyMetadata'][0]['CreateDate']
accesskeydate = accesskeydate.strftime("%Y-%m-%d %H:%M:%S")
currentdate = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
accesskeyd = time.mktime(datetime.datetime.strptime(accesskeydate, "%Y-%m-%d %H:%M:%S").timetuple())
currentd = time.mktime(datetime.datetime.strptime(currentdate, "%Y-%m-%d %H:%M:%S").timetuple())
active_days = (currentd - accesskeyd)/60/60/24
message = (key['UserName'],int(round(active_days))),
message = re.sub(r'[^a-zA-Z0-9 ]', "", str(message))
message = re.sub(r' ', ' is ', str(message))
if active_days >= 90:
sns_client.publish(
TopicArn='arn:aws:sns:us-west-2:xxxxxxxxxxxxx:topic-name',
Subject='User with Old Access Key Detected',
Message="The access key for " + str(message) + " days old. This user access key should be replaced ASAP.",
)
I'm struggling to get a Lambda function working. I have a python script to access twitter API, pull information, and export that information into an excel sheet. I'm trying to transfer python script over to AWS/Lambda, and I'm having a lot of trouble.
What I've done so far: Created AWS account, setup S3 to have a bucket, and poked around trying to get things to work.
I think the main area I'm struggling is how to go from a python script that I'm executing via local CLI and transforming that code into lambda-capable code. I'm not sure I understand how the lambda_handler function works, what the event or context arguments actually mean (despite watching a half dozen different tutorial videos), or how to integrate my existing functions into Lambda in the context of the lambda_handler, and I'm just very confused and hoping someone might be able to help me get some clarity!
Code that I'm using to pull twitter data (just a sample):
import time
import datetime
import keys
import pandas as pd
from twython import Twython, TwythonError
import pymysql
def lambda_handler(event, context):
def oauth_authenticate():
twitter_oauth = Twython(keys.APP_KEY, keys.APP_SECRET, oauth_version=2)
ACCESS_TOKEN = twitter_oauth.obtain_access_token()
twitter = Twython(keys.APP_KEY, access_token = ACCESS_TOKEN)
return twitter
def get_username():
"""
Prompts for the screen name of targetted account
"""
username = input("Enter the Twitter screenname you'd like information on. Do not include '#':")
return username
def get_user_followers(username):
"""
Returns data on all accounts following the targetted user.
WARNING: The number of followers can be huge, and the data isn't very valuable
"""
#username = get_username()
#import pdb; pdb.set_trace()
twitter = oauth_authenticate()
datestamp = str(datetime.datetime.now().strftime("%Y-%m-%d"))
target = twitter.lookup_user(screen_name = username)
for y in target:
target_id = y['id_str']
next_cursor = -1
index = 0
followersdata = {}
while next_cursor:
try:
get_followers = twitter.get_followers_list(screen_name = username,
count = 200,
cursor = next_cursor)
for x in get_followers['users']:
followersdata[index] = {}
followersdata[index]['screen_name'] = x['screen_name']
followersdata[index]['id_str'] = x['id_str']
followersdata[index]['name'] = x['name']
followersdata[index]['description'] = x['description']
followersdata[index]['date_checked'] = datestamp
followersdata[index]['targeted_account_id'] = target_id
index = index + 1
next_cursor = get_followers["next_cursor"]
except TwythonError as e:
print(e)
remainder = (float(twitter.get_lastfunction_header(header = 'x-rate-limit-reset')) \
- time.time())+1
print("Rate limit exceeded. Waiting for:", remainder/60, "minutes")
print("Current Time is:", time.strftime("%I:%M:%S"))
del twitter
time.sleep(remainder)
twitter = oauth_authenticate()
continue
followersDF = pd.DataFrame.from_dict(followersdata, orient = "index")
followersDF.to_excel("%s-%s-follower list.xlsx" % (username, datestamp),
index = False, encoding = 'utf-8')
I'm trying to get the access key age for each user in multiple AWS accounts. I currently have code that prints out the access key age but the code doesn't return it properly from an error. The code I'm working with is,
import boto3
from time import gmtime, strftime
from datetime import datetime
sts = boto3.client('sts')
def lambda_handler(event, context):
rolesessionname = "rolename"
account = "123456789"
response = sts.assume_role(
RoleArn = "arn:aws:iam::" + str(account) + ":role/audit",
RoleSessionName= rolesessionname
)
credentials = response['Credentials']
iam = boto3.client(
'iam',
aws_access_key_id = credentials['AccessKeyId'],
aws_secret_access_key = credentials['SecretAccessKey'],
aws_session_token = credentials['SessionToken']
)
response = iam.list_users()
nameList = []
todaysDate = strftime("%Y-%m-%d %H:%M:%S", gmtime())
todaysDate = str(todaysDate)
todaysDate = todaysDate[0:10]
todaysDate = datetime.strptime(todaysDate, "%Y-%m-%d")
for person in response["Users"]:
curPersonName = person["UserName"]
keys = iam.list_access_keys(UserName=curPersonName)
for keyData in keys["AccessKeyMetadata"]:
keyID = keyData["AccessKeyId"]
status = keyData["Status"]
CreateDate = keyData.get("CreateDate","none")
CreateDate = str(CreateDate)
CreateDate = CreateDate[0:10]
CreateDate = datetime.strptime(CreateDate, "%Y-%m-%d")
totalDays = abs((CreateDate - todaysDate).days)
print (totalDays-1)
nameList.append({
"UserName:":curPersonName,
"Status:": status,
"Create Date": CreateDate
#"Total days:" : totalDays-1
})
return nameList
My problem is that if I comment out the mentions of
CreateDate = datetime.strptime(CreateDate, "%Y-%m-%d")
and
totalDays = abs((CreateDate - todaysDate).days)
I get successful build and return data, just without the age of the key which is mainly what I want. However if I keep those lines in and print out to see if it's getting the age correctly it is. However It only prints them out and then errors with.
{
"errorMessage": "datetime.datetime(2017, 1, 11, 0, 0) is not JSON serializable",
"errorType": "TypeError",
"stackTrace": [
[
"/var/lang/lib/python3.6/json/__init__.py",
238,
"dumps",
"**kw).encode(obj)"
],
[
"/var/lang/lib/python3.6/json/encoder.py",
199,
"encode",
"chunks = self.iterencode(o, _one_shot=True)"
],
[
"/var/lang/lib/python3.6/json/encoder.py",
257,
"iterencode",
"return _iterencode(o, 0)"
],
[
"/var/runtime/awslambda/bootstrap.py",
110,
"decimal_serializer",
"raise TypeError(repr(o) + \" is not JSON serializable\")"
]
]
}
You're getting this error because datetime is not JSON serializable. You are saving a datetime object in CreateDate with
datetime.strptime(CreateDate, "%Y-%m-%d")
It seems like the answer would be to use this value to calculate your days:
totalDays = abs((CreateDate - todaysDate).days)
and then change it to a string representation before adding it to the return object:
CreateDate = CreateDate.isoformat() #or whatever return format you want.