runbooks 0.2.5__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conftest.py +26 -0
- jupyter-agent/.env +2 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/.gradio/certificate.pem +31 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/__main__.log +8 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
- jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/README.md +337 -0
- runbooks/finops/__init__.py +86 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +1004 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security/README.md +447 -0
- runbooks/security/__init__.py +71 -0
- runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
- runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
- runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
- runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
- runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
- runbooks/{security_baseline → security}/config-origin.json +1 -1
- runbooks/{security_baseline → security}/config.json +1 -1
- runbooks/{security_baseline → security}/permission.json +1 -1
- runbooks/{security_baseline → security}/report_generator.py +10 -2
- runbooks/{security_baseline → security}/report_template_en.html +7 -7
- runbooks/{security_baseline → security}/report_template_jp.html +7 -7
- runbooks/{security_baseline → security}/report_template_kr.html +12 -12
- runbooks/{security_baseline → security}/report_template_vn.html +7 -7
- runbooks/{security_baseline → security}/run_script.py +8 -2
- runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
- runbooks/{security_baseline → security}/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.7.0.dist-info/METADATA +375 -0
- runbooks-0.7.0.dist-info/RECORD +249 -0
- {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
- runbooks-0.7.0.dist-info/entry_points.txt +7 -0
- runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
- runbooks-0.7.0.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.5.dist-info/METADATA +0 -439
- runbooks-0.2.5.dist-info/RECORD +0 -61
- runbooks-0.2.5.dist-info/entry_points.txt +0 -3
- runbooks-0.2.5.dist-info/top_level.txt +0 -1
- /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
- /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
- /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
- /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
- /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
- /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
- /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
- /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
- /runbooks/{security_baseline → security}/utils/enums.py +0 -0
- /runbooks/{security_baseline → security}/utils/language.py +0 -0
- /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
- /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -0,0 +1,518 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
AWS IAM Role Inventory and Management Tool
|
4
|
+
|
5
|
+
CRITICAL SECURITY WARNING: This script can DELETE IAM roles across multiple AWS accounts.
|
6
|
+
Exercise extreme caution when using deletion features in production environments.
|
7
|
+
|
8
|
+
Purpose:
|
9
|
+
Discovers and inventories IAM roles across AWS Organizations with optional deletion
|
10
|
+
capabilities. Provides comprehensive role analysis with fragment-based filtering
|
11
|
+
and bulk management operations for enterprise IAM governance.
|
12
|
+
|
13
|
+
AWS API Operations:
|
14
|
+
- iam.list_roles(): Primary API for role discovery
|
15
|
+
- iam.list_attached_role_policies(): Policy attachment analysis
|
16
|
+
- iam.detach_role_policy(): Policy detachment for deletion
|
17
|
+
- iam.delete_role_policy(): Inline policy cleanup
|
18
|
+
- iam.delete_role(): Role deletion operation
|
19
|
+
|
20
|
+
Destructive Operations:
|
21
|
+
The +delete/+d flag enables IRREVERSIBLE role deletion across multiple accounts.
|
22
|
+
Includes safety confirmations and force options for automation scenarios.
|
23
|
+
|
24
|
+
Security Features:
|
25
|
+
- Multi-stage confirmation for destructive operations
|
26
|
+
- Comprehensive policy detachment before role deletion
|
27
|
+
- Force flag with delay for automation safety
|
28
|
+
- Detailed audit logging of all operations
|
29
|
+
|
30
|
+
Usage:
|
31
|
+
python list_iam_roles.py -p <profile> --fragment <role_name_fragment>
|
32
|
+
python list_iam_roles.py -p <profile> --fragment <fragment> +delete
|
33
|
+
|
34
|
+
Author: AWS Cloud Foundations Team
|
35
|
+
Version: 2023.11.06
|
36
|
+
Maintained: IAM Security Team
|
37
|
+
"""
|
38
|
+
|
39
|
+
import logging
|
40
|
+
import sys
|
41
|
+
from time import sleep, time
|
42
|
+
|
43
|
+
import boto3
|
44
|
+
from ArgumentsClass import CommonArguments
|
45
|
+
from botocore.exceptions import ClientError
|
46
|
+
from colorama import Fore, init
|
47
|
+
from Inventory_Modules import display_results, find_in, get_all_credentials
|
48
|
+
|
49
|
+
init()
|
50
|
+
__version__ = "2023.11.06"
|
51
|
+
|
52
|
+
|
53
|
+
###########################
|
54
|
+
def parse_args(args):
|
55
|
+
"""
|
56
|
+
Parse and validate command-line arguments for IAM role inventory operations.
|
57
|
+
|
58
|
+
Configures the argument parser with IAM-specific options including role fragment
|
59
|
+
filtering, deletion capabilities, and safety controls. Uses the standardized
|
60
|
+
CommonArguments framework for consistency across inventory scripts.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
args (list): Command-line arguments to parse (typically sys.argv[1:])
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
argparse.Namespace: Parsed arguments containing:
|
67
|
+
- Profiles: AWS profiles for multi-account access
|
68
|
+
- Regions: Target AWS regions (IAM is global but needed for sessions)
|
69
|
+
- Fragments: Role name fragments for filtering
|
70
|
+
- pDelete: DANGEROUS flag enabling role deletion
|
71
|
+
- Force: Skip confirmation prompts for automation
|
72
|
+
- Exact: Use exact matching instead of substring matching
|
73
|
+
- Other standard framework arguments
|
74
|
+
|
75
|
+
Security Note:
|
76
|
+
The pDelete parameter enables destructive operations that can impact
|
77
|
+
AWS account security posture. Use with extreme caution.
|
78
|
+
"""
|
79
|
+
parser = CommonArguments()
|
80
|
+
parser.my_parser.description = (
|
81
|
+
"We're going to find all roles within any of the accounts we have access to, given the profile(s) provided."
|
82
|
+
)
|
83
|
+
parser.multiprofile()
|
84
|
+
parser.multiregion()
|
85
|
+
parser.extendedargs()
|
86
|
+
parser.fragment()
|
87
|
+
parser.deletion()
|
88
|
+
parser.rootOnly()
|
89
|
+
parser.verbosity()
|
90
|
+
parser.timing()
|
91
|
+
parser.save_to_file()
|
92
|
+
parser.version(__version__)
|
93
|
+
parser.my_parser.add_argument(
|
94
|
+
"+d",
|
95
|
+
"+delete",
|
96
|
+
dest="pDelete",
|
97
|
+
action="store_const",
|
98
|
+
const=True,
|
99
|
+
default=False,
|
100
|
+
help="Whether you'd like to delete that specified role.",
|
101
|
+
)
|
102
|
+
return parser.my_parser.parse_args(args)
|
103
|
+
|
104
|
+
|
105
|
+
def my_delete_role(fRoleList):
|
106
|
+
"""
|
107
|
+
Execute comprehensive IAM role deletion with proper cleanup sequence.
|
108
|
+
|
109
|
+
CRITICAL SECURITY OPERATION: This function performs IRREVERSIBLE deletion
|
110
|
+
of IAM roles including all attached policies. Must follow AWS IAM deletion
|
111
|
+
sequence: detach managed policies -> delete inline policies -> delete role.
|
112
|
+
|
113
|
+
Args:
|
114
|
+
fRoleList (dict): Role deletion context containing:
|
115
|
+
- AccessKeyId, SecretAccessKey, SessionToken: AWS credentials
|
116
|
+
- Region: AWS region for session establishment
|
117
|
+
- RoleName: IAM role name to delete
|
118
|
+
- AccountId: Target AWS account (for logging)
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
bool: True if deletion successful, False if any step failed
|
122
|
+
|
123
|
+
Deletion Sequence:
|
124
|
+
1. Establish authenticated IAM session using provided credentials
|
125
|
+
2. List and detach all AWS managed and customer-managed policies
|
126
|
+
3. List and delete all inline policies attached to the role
|
127
|
+
4. Delete the IAM role itself (only works after all policies removed)
|
128
|
+
|
129
|
+
Error Handling:
|
130
|
+
- ClientError: AWS API errors (permissions, throttling, dependencies)
|
131
|
+
- Logs all errors for audit trail and troubleshooting
|
132
|
+
- Returns False on any failure to prevent inconsistent state
|
133
|
+
|
134
|
+
Security Implications:
|
135
|
+
- Role deletion affects all services and resources using the role
|
136
|
+
- Cannot be undone - permanent loss of role and policy associations
|
137
|
+
- May break applications, services, or automation depending on role
|
138
|
+
|
139
|
+
AWS IAM Constraints:
|
140
|
+
- Cannot delete role with attached policies (managed or inline)
|
141
|
+
- Cannot delete role if it has active sessions or is being assumed
|
142
|
+
- Some AWS service-linked roles cannot be deleted
|
143
|
+
"""
|
144
|
+
# Establish authenticated IAM session using provided credentials
|
145
|
+
iam_session = boto3.Session(
|
146
|
+
aws_access_key_id=fRoleList["AccessKeyId"],
|
147
|
+
aws_secret_access_key=fRoleList["SecretAccessKey"],
|
148
|
+
aws_session_token=fRoleList["SessionToken"],
|
149
|
+
region_name=fRoleList["Region"],
|
150
|
+
)
|
151
|
+
iam_client = iam_session.client("iam")
|
152
|
+
|
153
|
+
try:
|
154
|
+
# Step 1: Detach all managed policies (AWS managed and customer managed)
|
155
|
+
# These are policies attached via PolicyArn (not inline)
|
156
|
+
attached_role_policies = iam_client.list_attached_role_policies(RoleName=fRoleList["RoleName"])[
|
157
|
+
"AttachedPolicies"
|
158
|
+
]
|
159
|
+
|
160
|
+
for _ in range(len(attached_role_policies)):
|
161
|
+
response = iam_client.detach_role_policy(
|
162
|
+
RoleName=fRoleList["RoleName"], PolicyArn=attached_role_policies[_]["PolicyArn"]
|
163
|
+
)
|
164
|
+
logging.info(f"Detached policy {attached_role_policies[_]['PolicyArn']} from role {fRoleList['RoleName']}")
|
165
|
+
|
166
|
+
# Step 2: Delete all inline policies (policies embedded directly in the role)
|
167
|
+
# These are policy documents stored as part of the role definition
|
168
|
+
inline_role_policies = iam_client.list_role_policies(RoleName=fRoleList["RoleName"])["PolicyNames"]
|
169
|
+
|
170
|
+
for _ in range(len(inline_role_policies)):
|
171
|
+
response = iam_client.delete_role_policy(
|
172
|
+
RoleName=fRoleList["RoleName"],
|
173
|
+
PolicyName=inline_role_policies[_], # Fixed: was accessing dict incorrectly
|
174
|
+
)
|
175
|
+
logging.info(f"Deleted inline policy {inline_role_policies[_]} from role {fRoleList['RoleName']}")
|
176
|
+
|
177
|
+
# Step 3: Delete the IAM role itself (only works after all policies removed)
|
178
|
+
response = iam_client.delete_role(RoleName=fRoleList["RoleName"])
|
179
|
+
logging.warning(
|
180
|
+
f"DELETED IAM role {fRoleList['RoleName']} from account {fRoleList.get('AccountId', 'unknown')}"
|
181
|
+
)
|
182
|
+
|
183
|
+
return True
|
184
|
+
|
185
|
+
except ClientError as my_Error:
|
186
|
+
# Log detailed error for troubleshooting and audit purposes
|
187
|
+
logging.error(f"Failed to delete IAM role {fRoleList['RoleName']}: {my_Error}")
|
188
|
+
|
189
|
+
# Common failure scenarios:
|
190
|
+
# - Role has active sessions or is being assumed
|
191
|
+
# - Role has instance profiles attached
|
192
|
+
# - Permissions insufficient for deletion operations
|
193
|
+
# - Role is service-linked and managed by AWS service
|
194
|
+
|
195
|
+
return False
|
196
|
+
|
197
|
+
|
198
|
+
def find_and_collect_roles_across_accounts(fAllCredentials: list, frole_fragments: list) -> list:
|
199
|
+
"""
|
200
|
+
Execute sequential IAM role discovery across multiple AWS accounts and regions.
|
201
|
+
|
202
|
+
WARNING: This function processes accounts sequentially and should be enhanced
|
203
|
+
with multi-threading for better performance in large AWS Organizations.
|
204
|
+
|
205
|
+
Performs comprehensive IAM role inventory across all provided accounts,
|
206
|
+
with optional fragment-based filtering for targeted role discovery.
|
207
|
+
Handles AWS IAM API pagination and provides real-time progress feedback.
|
208
|
+
|
209
|
+
Args:
|
210
|
+
fAllCredentials (list): List of credential dictionaries containing:
|
211
|
+
- AccessKeyId, SecretAccessKey, SessionToken: AWS credentials
|
212
|
+
- Region: AWS region (IAM is global, but needed for session)
|
213
|
+
- AccountNumber: AWS account ID for role attribution
|
214
|
+
- MgmtAccount: Management account identifier
|
215
|
+
- Success: Boolean indicating credential validation status
|
216
|
+
|
217
|
+
frole_fragments (list): Optional role name fragments for filtering.
|
218
|
+
If None, returns all roles. If provided, filters by fragment matching.
|
219
|
+
|
220
|
+
Returns:
|
221
|
+
list: Comprehensive list of IAM role dictionaries containing:
|
222
|
+
- RoleName: IAM role name
|
223
|
+
- AccountId: Source AWS account ID
|
224
|
+
- MgmtAcct: Management account identifier
|
225
|
+
- Region: AWS region (for session context)
|
226
|
+
- AccessKeyId, SecretAccessKey, SessionToken: Credentials for operations
|
227
|
+
|
228
|
+
Performance Considerations:
|
229
|
+
- Sequential processing may be slow for large Organizations
|
230
|
+
- IAM list_roles() API supports pagination for accounts with many roles
|
231
|
+
- Real-time progress display shows per-account role counts
|
232
|
+
|
233
|
+
Error Handling:
|
234
|
+
- Skips accounts where credential validation failed
|
235
|
+
- AuthFailure: Logs and continues with remaining accounts
|
236
|
+
- ClientError: Handles AWS API errors gracefully
|
237
|
+
- Pagination: Properly handles truncated IAM responses
|
238
|
+
|
239
|
+
Filtering Logic:
|
240
|
+
- If frole_fragments is None: Returns all discovered roles
|
241
|
+
- If frole_fragments provided: Uses find_in() with exact/partial matching
|
242
|
+
- Exact matching controlled by global pExact flag
|
243
|
+
|
244
|
+
TODO: Implement multi-threading pattern similar to other inventory scripts
|
245
|
+
for improved performance across large AWS Organizations.
|
246
|
+
"""
|
247
|
+
print()
|
248
|
+
if pFragments is None:
|
249
|
+
print(f"Listing out all roles across {len(fAllCredentials)} accounts")
|
250
|
+
print()
|
251
|
+
elif pExact:
|
252
|
+
print(
|
253
|
+
f"Looking for a role {Fore.RED}exactly{Fore.RESET} named one of these strings {frole_fragments} across {len(fAllCredentials)} accounts"
|
254
|
+
)
|
255
|
+
print()
|
256
|
+
else:
|
257
|
+
print(
|
258
|
+
f"Looking for a role containing one of these strings {frole_fragments} across {len(fAllCredentials)} accounts"
|
259
|
+
)
|
260
|
+
print()
|
261
|
+
|
262
|
+
# Initialize role collection list for aggregating results across all accounts
|
263
|
+
Roles = []
|
264
|
+
|
265
|
+
# Sequential processing of each account (TODO: convert to multi-threading)
|
266
|
+
for account in fAllCredentials:
|
267
|
+
# Skip accounts where credential validation failed during setup
|
268
|
+
if account["Success"]:
|
269
|
+
# Establish authenticated IAM session for this account
|
270
|
+
# Note: IAM is a global service but session requires region parameter
|
271
|
+
iam_session = boto3.Session(
|
272
|
+
aws_access_key_id=account["AccessKeyId"],
|
273
|
+
aws_secret_access_key=account["SecretAccessKey"],
|
274
|
+
aws_session_token=account["SessionToken"],
|
275
|
+
region_name=account["Region"], # Required for session, though IAM is global
|
276
|
+
)
|
277
|
+
iam_client = iam_session.client("iam")
|
278
|
+
else:
|
279
|
+
# Skip this account due to credential validation failure
|
280
|
+
continue
|
281
|
+
|
282
|
+
try:
|
283
|
+
# Call AWS IAM API to list roles in this account
|
284
|
+
# Default limit is 100 roles per call, pagination handled below
|
285
|
+
response = iam_client.list_roles()
|
286
|
+
|
287
|
+
# Process first page of roles
|
288
|
+
for i in range(len(response["Roles"])):
|
289
|
+
# Create standardized role record with metadata and credentials
|
290
|
+
# Credentials included to enable subsequent operations (deletion, etc.)
|
291
|
+
Roles.append(
|
292
|
+
{
|
293
|
+
# AWS credentials for role operations
|
294
|
+
"AccessKeyId": account["AccessKeyId"],
|
295
|
+
"SecretAccessKey": account["SecretAccessKey"],
|
296
|
+
"SessionToken": account["SessionToken"],
|
297
|
+
# Organizational and account context
|
298
|
+
"MgmtAcct": account["MgmtAccount"],
|
299
|
+
"Region": account["Region"],
|
300
|
+
"AccountId": account["AccountNumber"],
|
301
|
+
# IAM role metadata from AWS API response
|
302
|
+
"RoleName": response["Roles"][i]["RoleName"],
|
303
|
+
}
|
304
|
+
)
|
305
|
+
|
306
|
+
# Track role count for progress display
|
307
|
+
num_of_roles_in_account = len(response["Roles"])
|
308
|
+
|
309
|
+
# Handle AWS IAM API pagination for accounts with >100 roles
|
310
|
+
# Enterprise accounts often have hundreds of roles requiring pagination
|
311
|
+
while response["IsTruncated"]:
|
312
|
+
# Get next page using pagination marker
|
313
|
+
response = iam_client.list_roles(Marker=response["Marker"])
|
314
|
+
|
315
|
+
# Process additional roles from paginated response
|
316
|
+
for i in range(len(response["Roles"])):
|
317
|
+
Roles.append(
|
318
|
+
{
|
319
|
+
"AccessKeyId": account["AccessKeyId"],
|
320
|
+
"SecretAccessKey": account["SecretAccessKey"],
|
321
|
+
"SessionToken": account["SessionToken"],
|
322
|
+
"MgmtAcct": account["MgmtAccount"],
|
323
|
+
"Region": account["Region"],
|
324
|
+
"AccountId": account["AccountNumber"],
|
325
|
+
"RoleName": response["Roles"][i]["RoleName"],
|
326
|
+
}
|
327
|
+
)
|
328
|
+
|
329
|
+
# Update role count (note: this shows last page count, not total)
|
330
|
+
num_of_roles_in_account += len(response["Roles"])
|
331
|
+
|
332
|
+
# Display real-time progress with role count per account
|
333
|
+
print(f"Found {num_of_roles_in_account} roles in account {account['AccountNumber']}", end="\r")
|
334
|
+
|
335
|
+
except ClientError as my_Error:
|
336
|
+
# Handle AWS IAM API errors with appropriate logging
|
337
|
+
if "AuthFailure" in str(my_Error):
|
338
|
+
print(f"\nAuthorization Failure for account {account['AccountId']}")
|
339
|
+
# Common causes: insufficient IAM permissions, account access issues
|
340
|
+
else:
|
341
|
+
print(f"\nError: {my_Error}")
|
342
|
+
# Other potential errors: throttling, service unavailability, etc.
|
343
|
+
|
344
|
+
# Apply role name filtering based on provided fragments
|
345
|
+
if pFragments is None:
|
346
|
+
# No filtering requested - return all discovered roles
|
347
|
+
found_roles = Roles
|
348
|
+
else:
|
349
|
+
# Filter roles using fragment matching (exact or partial based on pExact flag)
|
350
|
+
# find_in() handles both exact string matching and substring matching
|
351
|
+
found_roles = [x for x in Roles if find_in([x["RoleName"]], pFragments, pExact)]
|
352
|
+
|
353
|
+
return found_roles
|
354
|
+
|
355
|
+
|
356
|
+
def delete_roles(roles_to_delete):
|
357
|
+
"""
|
358
|
+
Execute batch IAM role deletion with comprehensive safety controls.
|
359
|
+
|
360
|
+
CRITICAL SECURITY FUNCTION: This orchestrates IRREVERSIBLE deletion of IAM roles
|
361
|
+
across multiple AWS accounts. Implements multiple safety layers including
|
362
|
+
confirmation prompts, force flags, and detailed audit logging.
|
363
|
+
|
364
|
+
Args:
|
365
|
+
roles_to_delete (list): List of role dictionaries to delete, each containing:
|
366
|
+
- RoleName: IAM role name for deletion
|
367
|
+
- AccountId: Target AWS account ID
|
368
|
+
- Credential information for authenticated API calls
|
369
|
+
|
370
|
+
Safety Control Flow:
|
371
|
+
1. Fragment Validation: Refuses deletion if no role fragments specified
|
372
|
+
2. Interactive Confirmation: Prompts user for explicit confirmation
|
373
|
+
3. Force Mode: Automated confirmation with safety delay
|
374
|
+
4. Individual Deletion: Calls my_delete_role() for each role
|
375
|
+
5. Result Tracking: Updates role records with deletion status
|
376
|
+
|
377
|
+
Security Features:
|
378
|
+
- Prevents accidental bulk deletion without fragment specification
|
379
|
+
- Interactive confirmation shows exact count and scope
|
380
|
+
- Force mode includes mandatory delay allowing Ctrl-C abort
|
381
|
+
- Per-role deletion status tracking for audit purposes
|
382
|
+
- Comprehensive logging of all deletion attempts
|
383
|
+
|
384
|
+
Deletion Modes:
|
385
|
+
- Interactive Mode: User must confirm each batch deletion
|
386
|
+
- Force Mode: Automated with {time_to_sleep} second safety delay
|
387
|
+
- Dry Run: No deletion if pDelete flag not set
|
388
|
+
|
389
|
+
Side Effects:
|
390
|
+
- Modifies roles_to_delete list by adding 'Action' field
|
391
|
+
- Logs all deletion attempts at INFO level
|
392
|
+
- Displays progress and confirmation prompts to stdout
|
393
|
+
|
394
|
+
Error Handling:
|
395
|
+
- Individual role deletion failures are logged but don't stop batch
|
396
|
+
- Failed deletions marked with "delete failed" status
|
397
|
+
- Successful deletions marked with "deleted" status
|
398
|
+
|
399
|
+
Enterprise Considerations:
|
400
|
+
- Supports bulk operations across hundreds of roles
|
401
|
+
- Maintains audit trail for compliance requirements
|
402
|
+
- Provides abort mechanisms for operational safety
|
403
|
+
"""
|
404
|
+
confirm = False
|
405
|
+
|
406
|
+
if pDelete:
|
407
|
+
# Safety Check #1: Prevent bulk deletion without specific targeting
|
408
|
+
if pFragments is None:
|
409
|
+
print(
|
410
|
+
f"You asked to delete roles, but didn't give a specific role to delete, so we're not going to delete anything."
|
411
|
+
)
|
412
|
+
|
413
|
+
# Safety Check #2: Interactive confirmation for targeted deletions
|
414
|
+
elif len(roles_to_delete) > 0 and not pForce:
|
415
|
+
print(
|
416
|
+
f"Your specified role fragment matched at least 1 role.\n"
|
417
|
+
f"Please confirm you want to really delete all {len(roles_to_delete)} roles found"
|
418
|
+
)
|
419
|
+
confirm = (
|
420
|
+
input(
|
421
|
+
f"Really delete {len(roles_to_delete)} across {len(AllCredentials)} accounts. Are you still sure? (y/n): "
|
422
|
+
).lower()
|
423
|
+
== "y"
|
424
|
+
)
|
425
|
+
|
426
|
+
# Safety Check #3: Force mode with mandatory safety delay
|
427
|
+
elif pForce and len(roles_to_delete) > 0:
|
428
|
+
print(
|
429
|
+
f"You specified a fragment that matched multiple roles.\n"
|
430
|
+
f"And you specified the 'FORCE' parameter - so we're not asking again, BUT we'll wait {time_to_sleep} seconds to give you the option to Ctrl-C here..."
|
431
|
+
)
|
432
|
+
# Mandatory delay provides abort opportunity even in automation
|
433
|
+
sleep(time_to_sleep)
|
434
|
+
|
435
|
+
# Execute batch deletion if safety controls are satisfied
|
436
|
+
if (pDelete and confirm) or (pDelete and pForce):
|
437
|
+
for i in range(len(roles_to_delete)):
|
438
|
+
# Log each deletion attempt for audit purposes
|
439
|
+
logging.info(
|
440
|
+
f"Deleting role {roles_to_delete[i]['RoleName']} from account {roles_to_delete[i]['AccountId']}"
|
441
|
+
)
|
442
|
+
|
443
|
+
# Attempt individual role deletion
|
444
|
+
result = my_delete_role(roles_to_delete[i])
|
445
|
+
|
446
|
+
# Update role record with deletion status for reporting
|
447
|
+
if result:
|
448
|
+
roles_to_delete[i].update({"Action": "deleted"})
|
449
|
+
else:
|
450
|
+
roles_to_delete[i].update({"Action": "delete failed"})
|
451
|
+
|
452
|
+
|
453
|
+
###########################
|
454
|
+
|
455
|
+
if __name__ == "__main__":
|
456
|
+
args = parse_args(sys.argv[1:])
|
457
|
+
pProfiles = args.Profiles
|
458
|
+
pRegionList = args.Regions
|
459
|
+
# pRole = args.pRole
|
460
|
+
pFragments = args.Fragments
|
461
|
+
pAccounts = args.Accounts
|
462
|
+
pSkipAccounts = args.SkipAccounts
|
463
|
+
pSkipProfiles = args.SkipProfiles
|
464
|
+
pDelete = args.pDelete
|
465
|
+
pForce = args.Force
|
466
|
+
pExact = args.Exact
|
467
|
+
pRootOnly = args.RootOnly
|
468
|
+
pFilename = args.Filename
|
469
|
+
pTiming = args.Time
|
470
|
+
verbose = args.loglevel
|
471
|
+
# Setup logging levels
|
472
|
+
logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
473
|
+
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
474
|
+
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
475
|
+
logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
|
476
|
+
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
477
|
+
|
478
|
+
ERASE_LINE = "\x1b[2K"
|
479
|
+
time_to_sleep = 5
|
480
|
+
begin_time = time()
|
481
|
+
|
482
|
+
print()
|
483
|
+
|
484
|
+
# Get credentials for all Child Accounts
|
485
|
+
AllCredentials = get_all_credentials(
|
486
|
+
pProfiles, pTiming, pSkipProfiles, pSkipAccounts, pRootOnly, pAccounts, pRegionList
|
487
|
+
)
|
488
|
+
# Collect the stacksets, AccountList and RegionList involved
|
489
|
+
all_found_roles = find_and_collect_roles_across_accounts(AllCredentials, pFragments)
|
490
|
+
# Display the information we've found this far
|
491
|
+
sorted_Results = sorted(all_found_roles, key=lambda d: (d["MgmtAcct"], d["AccountId"], d["RoleName"]))
|
492
|
+
display_dict = {
|
493
|
+
"AccountId": {"DisplayOrder": 2, "Heading": "Account Number"},
|
494
|
+
"MgmtAcct": {"DisplayOrder": 1, "Heading": "Parent Acct"},
|
495
|
+
"RoleName": {"DisplayOrder": 3, "Heading": "Role Name"},
|
496
|
+
"Action": {"DisplayOrder": 4, "Heading": "Action Taken"},
|
497
|
+
}
|
498
|
+
|
499
|
+
display_results(sorted_Results, display_dict, "No Action", pFilename)
|
500
|
+
|
501
|
+
# Modify stacks, if requested
|
502
|
+
if pDelete:
|
503
|
+
delete_roles(sorted_Results)
|
504
|
+
|
505
|
+
print()
|
506
|
+
if pFragments is None:
|
507
|
+
print(f"Found {len(sorted_Results)} roles across {len(AllCredentials)} accounts")
|
508
|
+
else:
|
509
|
+
print(
|
510
|
+
f"Found {len(sorted_Results)} instances where role containing {pFragments} was found across {len(AllCredentials)} accounts"
|
511
|
+
)
|
512
|
+
|
513
|
+
if pTiming:
|
514
|
+
print(ERASE_LINE)
|
515
|
+
print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
|
516
|
+
print()
|
517
|
+
print("Thanks for using this script...")
|
518
|
+
print()
|