11"""
2+
23Generate a JSON dump of IAM permissions for a given role.
4+ Anything that looks like an account ID (12 digit number) will cause the script to fail,
5+ to prevent committing sensitive information to a repo.
6+
7+ Sensitive account IDs can be found/replaced with aliases using the command line arguments.
8+ The replaced values will be in the format ${alias}.
9+
10+ Usage:
11+ python export_role_policies.py <role_name> [<find>=<replace> ...]
12+
313"""
414
515import json
616import re
17+ import sys
718
819from argparse import ArgumentParser
920from collections import defaultdict
@@ -42,7 +53,7 @@ def get_attached_role_policy(client, policy_arn):
4253 return version_response ['PolicyVersion' ]['Document' ]['Statement' ]
4354
4455
45- def main (account , role ):
56+ def main (role , aliases ):
4657 client = boto3 .client ('iam' )
4758 policy_map = defaultdict (lambda : defaultdict (dict ))
4859
@@ -51,20 +62,35 @@ def main(account, role):
5162 permissions = get_role_policy (client , role , policy_name )
5263 policy_map ['inline' ][policy_name ] = permissions
5364
54- # # Get attached policies for the role
65+ # Get attached policies for the role
5566 for attached_policy in list_attached_role_policies (client , role ):
5667 policy_name = attached_policy ['PolicyName' ]
5768 policy_arn = attached_policy ['PolicyArn' ]
5869 permissions = get_attached_role_policy (client , policy_arn )
5970 policy_map ['attached' ][policy_name ] = permissions
6071
6172 json_text = json .dumps (policy_map , indent = 2 )
62- print (re .sub (account , "${account_id}" , json_text ))
73+ for search , replace in aliases .items ():
74+ json_text = re .sub (search , f"${{{ replace } }}" , json_text )
75+
76+ # Fail on anything that looks like an account ID
77+ # e.g. :123456789012:
78+ matches = set (re .findall (r"\D(\d{12})\D" , json_text ))
79+ if matches :
80+ print ("Warning! Found potential account IDs in policies.\n "
81+ "These should be replaced with an alias before committing to a repo:\n "
82+ f"{ ', ' .join (matches )} " ,
83+ file = sys .stderr )
84+ sys .exit (1 )
85+
86+ print (json_text )
6387
6488
6589if __name__ == "__main__" :
6690 parser = ArgumentParser (description = "Export IAM policies for a given role and account" )
67- parser .add_argument ("account" , help = "Account ID to replace in output with ${account_id}" )
6891 parser .add_argument ("role" , help = "Role to export" )
92+ parser .add_argument ("aliases" , nargs = '*' ,
93+ help = "A list of aliases to apply. E.g. 123456789012=prod_account" )
6994 args = parser .parse_args ()
70- main (args .account , args .role )
95+ alias_map = {alias .split ('=' )[0 ]: alias .split ('=' )[1 ] for alias in args .aliases }
96+ main (args .role , alias_map )
0 commit comments