def __init__(self, account_id): self.session = boto3.Session() self.iam = BotoFactory().get_capability(boto3.resource, self.session, 'iam', account_id=account_id) self.interesting_roles = None self.users_w_used_passwords = None
def main(event, context): """ Updates the metadata DynamoDB table with Organizations as source of truth, Must have an existing table to run (from .env) If there is no table, run make_table.py """ if event.get('profile_name'): session = boto3.Session(profile_name=event.get('profile_name')) else: session = boto3.Session() # check if DDB table exists, if not create it ddb_r = BotoFactory().get_capability(boto3.resource, session, 'dynamodb') table = ddb_r.Table(os.environ['TABLE_NAME']) try: table.creation_date_time except ddb_r.meta.client.exceptions.ResourceNotFoundException: logging.error('Table does not exist, creating') logging.info(DynamoDBOps().make_metadata_table( ddb_r.meta.client, os.environ['TABLE_NAME'])) table.wait_until_exists() logging.info(f"Table created: {table.creation_date_time}") # create dictionary of all ACTIVE organization accounts accounts = AccountOps().get_accounts(session) ddb = BotoFactory().get_capability(boto3.client, session, 'dynamodb') # update dynamoDB with account information DynamoDBOps().populate_baseline_metadata(ddb, os.environ['TABLE_NAME'], accounts) # make roleswitch URLs for id in accounts: DynamoDBOps().add_roleswitch_url(ddb, os.environ['TABLE_NAME'], id, __make_roleswitch_url(accounts[id])) # make HTML file column_order = ['AccountId', 'Name', 'RoleSwitchURL'] local_path = f"/tmp/{os.environ['DEFAULT_FILENAME']}" logging.info( DDBToHTML(session).make_html_file(column_order=column_order, filename=local_path)) # put in S3, get pre-signed URL object_name = 'metadata.html' S3Ops(session).put_html_file(local_path, os.environ['HTML_BUCKET'], object_name) url = S3Ops(session).create_presigned_url(os.environ['S3_READONLY_ROLE'], os.environ['HTML_BUCKET'], object_name) response = {'success': True, 'url': url} return {'statusCode': 200, 'body': json.dumps(response)}
def get_accounts(self, session): org = BotoFactory().get_capability(boto3.client, session, 'organizations') accounts = dict() paginator = org.get_paginator('list_accounts') itr = paginator.paginate() for i in itr: for account in i['Accounts']: accounts[account['Id']] = account return accounts
def check_acount_for_billing_access(self, session, account_id): costexplorer = BotoFactory().get_capability(boto3.client, session, 'ce', account_id=account_id) response = costexplorer.get_cost_and_usage(TimePeriod={ 'Start': '2019-08-01', 'End': '2019-09-01' }, Granularity='MONTHLY', Metrics=['BlendedCost']) return response
def get_cost_timespan(self, start, end): ce = BotoFactory().get_capability( boto3.client, self.session, 'ce', self.account_id ) return ce.get_cost_and_usage( TimePeriod={ 'Start': start, 'End': end }, Granularity='MONTHLY', Metrics=['UnblendedCost'] )['ResultsByTime']
def __init__(self, session): if os.environ.get('EXCLUDE_OUS'): self.blocklist = os.environ.get('EXCLUDE_OUS').split(',') log.info(f"Exclude OU:s: {self.blocklist}") else: log.warn( f"No EXCLUDE_OUS present in ENV, all OUs can be traversed" ) self.blocklist = set() self.org = BotoFactory().get_capability( boto3.client, session, 'organizations' )
def __get_client(self, rolename=''): if rolename == '': rolename = os.environ['DEFAULT_ROLE'] return BotoFactory().get_capability(boto3.client, self.session, 's3', rolename=rolename)
def main(fqdn, source, target, profile='', verify=''): if (fqdn or source or target) is None: print("Missing parameters, use --help") sys.exit(1) if profile == '': profile = 'default' # FQDN must end with ., add if missing if not fqdn.endswith('.'): fqdn = fqdn + '.' # make Route53 resources r53ops = Route53Ops(fqdn) session = boto3.Session(profile_name=profile) r53_source = BotoFactory().get_capability( boto3.client, session, 'route53', account_id=source ) r53_target = BotoFactory().get_capability( boto3.client, session, 'route53', account_id=target ) try: source_records = r53ops.get_all_records_from_hosted_zone(r53_source) except AttributeError as e: sys.stdout.write(f"{fqdn} doesn't appear to exist in {source}") sys.exit(1) # if not verify, run put if verify != 'true': r53ops.put_records(r53_target, source_records) # if verify, then only run verify and skip put try: target_records = r53ops.get_all_records_from_hosted_zone(r53_target) except AttributeError as e: sys.stdout.write(f"{fqdn} doesn't appear to exist in {target}") sys.exit(1) __json_print(check_records_similarity( fqdn, source_records, target_records ))
def check_account_for_roles(self, session, account_id, roles): for r in roles: try: BotoFactory().get_capability(boto3.client, session, 'sts', account_id, rolename=r) roles[r] = True except ClientError as e: if e.response['Error']['Code'] == 'AccessDenied': logging.info(f"{r} did not exist in {account_id}") pass return roles
class IAMOps(): def __init__(self, account_id): self.session = boto3.Session() self.iam = BotoFactory().get_capability(boto3.resource, self.session, 'iam', account_id=account_id) self.interesting_roles = None self.users_w_used_passwords = None def get_interesting_roles(self): # returns all Roles as iam.Role resource # .all() method Roles don't include role_last_used attribute!! roles = self.iam.roles.all() interesting_roles = list() for r in roles: if '/aws-service-role/' in r.arn or r.name in whitelisted_roles: pass else: for p in r.assume_role_policy_document.get('Statement'): if p.get('Principal').get('Service'): pass else: log.debug(f"Appending to interesting roles: {r.arn}") interesting_roles.append(r) self.interesting_roles = interesting_roles return interesting_roles def get_timestamped_roles(self): if not self.interesting_roles: self.interesting_roles = self.get_interesting_roles() timestamped_roles = list() for r in self.interesting_roles: role = self.iam.Role(r.name) if role.role_last_used: timestamped_roles.append(role) return timestamped_roles def get_iam_users_w_used_password(self): # returns all users as iam.User resource self.users_w_used_passwords = [ user for user in self.iam.users.all() if user.password_last_used ] return self.users_w_used_passwords
def __init__(self, session): self.ddb = BotoFactory().get_capability(boto3.client, session, 'dynamodb')
class DDBToHTML(): def __init__(self, session): self.ddb = BotoFactory().get_capability(boto3.client, session, 'dynamodb') def table_to_list(self): paginator = self.ddb.get_paginator('scan') itr = paginator.paginate(TableName=os.environ['TABLE_NAME'], Select='ALL_ATTRIBUTES') all_items = list() for i in itr: all_items = i['Items'] + all_items return all_items def get_all_attributes(self, list_ddb_items): """expects a list of dictionaries where each dictionary is a single item from DynamoDB""" attributes = set() for i in list_ddb_items: attributes = attributes.union(i.keys()) return attributes def make_order_of_columns(self, attributes, desired_order_list): """takes input of attributes set and a partial or complete list of attributes in the desired order for the columns, e.g. should AccountId be first column then desired_order_list=['AccountId'] If the desired order has fewer items than the attributes they will be added randomly depending on the order of the set returns list of order which can be used for HTML table generation """ order_list = list() for attr in desired_order_list: try: attributes.remove(attr) order_list.append(attr) except Exception as e: raise (e) if len(attributes) > 0: for i in range(len(attributes)): order_list.append(attributes.pop()) return order_list def make_table_head(self, ordered_list): thead = '<thead>\n\t\t\t<tr>\n' th_row = '\t\t\t\t<th>\n\t\t\t\t\t<p>%s\n\t\t\t\t</th>\n' for i in range(len(ordered_list)): thead += th_row % ordered_list[i] thead += '\t\t\t</tr>\n\t\t</thead>\n' return thead def make_table_row(self, ordered_list, account_info): tr = '\t\t<tr>\n' tr_td = "\t\t\t<td>\n\t\t\t\t<p>%s\n\t\t\t</td>\n" for i in range(len(ordered_list)): data = account_info.get(ordered_list[i])['S'] if data.startswith('https://'): data = "<a href=\"%s\" target=_blank>%s</a>" % (data, data) tr += tr_td % data tr += '\t\t</tr>\n' return tr def make_html_file(self, column_order='', filename=''): if column_order == '': self.logging.warning('No column order specified') column_order = [] if filename == '': filename = os.environ['DEFAULT_FILENAME'] items = self.table_to_list() attributes = self.get_all_attributes(items) ordered_list = self.make_order_of_columns(attributes, column_order) thead = self.make_table_head(ordered_list) html_rows = str() for item in items: html_rows += self.make_table_row(ordered_list, item) params = { 'TITLE': 'Account Roleswitching and Metadata', 'THEAD_VALS': thead, 'TR_VALS': html_rows } with open('html_templating/table_template.html', 'r') as f: template = f.read() path = filename with open(path, 'w') as out: out.write(template % params) return ({'Status': 'OK', 'Path': path})
class OrgOps(): def __init__(self, session): if os.environ.get('EXCLUDE_OUS'): self.blocklist = os.environ.get('EXCLUDE_OUS').split(',') log.info(f"Exclude OU:s: {self.blocklist}") else: log.warn( f"No EXCLUDE_OUS present in ENV, all OUs can be traversed" ) self.blocklist = set() self.org = BotoFactory().get_capability( boto3.client, session, 'organizations' ) def get_active_accounts(self): accounts = dict() paginator = self.org.get_paginator('list_accounts') itr = paginator.paginate() for i in itr: for account in i['Accounts']: if account['Status'] == 'ACTIVE': accounts[account['Id']] = account return accounts def get_all_children_ou(self, parent_ou): ous = set() log.info(f"Getting children OU for {parent_ou}") pgnt = self.org.get_paginator('list_organizational_units_for_parent') itr = pgnt.paginate( ParentId=parent_ou ) for i in itr: for ou in i['OrganizationalUnits']: if ou['Id'] not in self.blocklist: ous.add(ou['Id']) if ous: for ou in ous.copy(): ous.update(self.get_all_children_ou(ou)) return ous def get_active_accounts_from_ous(self, ous): pgnt = self.org.get_paginator('list_accounts_for_parent') accounts = dict() for ou in ous: log.info(f"Getting accounts from {ou}") itr = pgnt.paginate(ParentId=ou) for i in itr: for account in i['Accounts']: if account['Status'] == 'ACTIVE': accounts[account['Id']] = account return accounts def get_accounts_from_root(self): root_ou = self.org.list_roots()['Roots'][0]['Id'] return self.get_active_accounts_from_ous( self.get_all_children_ou(root_ou) )