runbooks 0.7.0__py3-none-any.whl → 0.7.6__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.
- runbooks/__init__.py +87 -37
- runbooks/cfat/README.md +300 -49
- runbooks/cfat/__init__.py +2 -2
- runbooks/finops/__init__.py +1 -1
- runbooks/finops/cli.py +1 -1
- runbooks/inventory/collectors/__init__.py +8 -0
- runbooks/inventory/collectors/aws_management.py +791 -0
- runbooks/inventory/collectors/aws_networking.py +3 -3
- runbooks/main.py +3389 -782
- runbooks/operate/__init__.py +207 -0
- runbooks/operate/base.py +311 -0
- runbooks/operate/cloudformation_operations.py +619 -0
- runbooks/operate/cloudwatch_operations.py +496 -0
- runbooks/operate/dynamodb_operations.py +812 -0
- runbooks/operate/ec2_operations.py +926 -0
- runbooks/operate/iam_operations.py +569 -0
- runbooks/operate/s3_operations.py +1211 -0
- runbooks/operate/tagging_operations.py +655 -0
- runbooks/remediation/CLAUDE.md +100 -0
- runbooks/remediation/DOME9.md +218 -0
- runbooks/remediation/README.md +26 -0
- runbooks/remediation/Tests/__init__.py +0 -0
- runbooks/remediation/Tests/update_policy.py +74 -0
- runbooks/remediation/__init__.py +95 -0
- runbooks/remediation/acm_cert_expired_unused.py +98 -0
- runbooks/remediation/acm_remediation.py +875 -0
- runbooks/remediation/api_gateway_list.py +167 -0
- runbooks/remediation/base.py +643 -0
- runbooks/remediation/cloudtrail_remediation.py +908 -0
- runbooks/remediation/cloudtrail_s3_modifications.py +296 -0
- runbooks/remediation/cognito_active_users.py +78 -0
- runbooks/remediation/cognito_remediation.py +856 -0
- runbooks/remediation/cognito_user_password_reset.py +163 -0
- runbooks/remediation/commons.py +455 -0
- runbooks/remediation/dynamodb_optimize.py +155 -0
- runbooks/remediation/dynamodb_remediation.py +744 -0
- runbooks/remediation/dynamodb_server_side_encryption.py +108 -0
- runbooks/remediation/ec2_public_ips.py +134 -0
- runbooks/remediation/ec2_remediation.py +892 -0
- runbooks/remediation/ec2_subnet_disable_auto_ip_assignment.py +72 -0
- runbooks/remediation/ec2_unattached_ebs_volumes.py +448 -0
- runbooks/remediation/ec2_unused_security_groups.py +202 -0
- runbooks/remediation/kms_enable_key_rotation.py +651 -0
- runbooks/remediation/kms_remediation.py +717 -0
- runbooks/remediation/lambda_list.py +243 -0
- runbooks/remediation/lambda_remediation.py +971 -0
- runbooks/remediation/multi_account.py +569 -0
- runbooks/remediation/rds_instance_list.py +199 -0
- runbooks/remediation/rds_remediation.py +873 -0
- runbooks/remediation/rds_snapshot_list.py +192 -0
- runbooks/remediation/requirements.txt +118 -0
- runbooks/remediation/s3_block_public_access.py +159 -0
- runbooks/remediation/s3_bucket_public_access.py +143 -0
- runbooks/remediation/s3_disable_static_website_hosting.py +74 -0
- runbooks/remediation/s3_downloader.py +215 -0
- runbooks/remediation/s3_enable_access_logging.py +562 -0
- runbooks/remediation/s3_encryption.py +526 -0
- runbooks/remediation/s3_force_ssl_secure_policy.py +143 -0
- runbooks/remediation/s3_list.py +141 -0
- runbooks/remediation/s3_object_search.py +201 -0
- runbooks/remediation/s3_remediation.py +816 -0
- runbooks/remediation/scan_for_phrase.py +425 -0
- runbooks/remediation/workspaces_list.py +220 -0
- runbooks/security/__init__.py +9 -10
- runbooks/security/security_baseline_tester.py +4 -2
- runbooks-0.7.6.dist-info/METADATA +608 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/RECORD +84 -76
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/entry_points.txt +0 -1
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/top_level.txt +0 -1
- jupyter-agent/.env +0 -2
- jupyter-agent/.env.template +0 -2
- jupyter-agent/.gitattributes +0 -35
- jupyter-agent/.gradio/certificate.pem +0 -31
- jupyter-agent/README.md +0 -16
- jupyter-agent/__main__.log +0 -8
- jupyter-agent/app.py +0 -256
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +0 -154
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +0 -123
- jupyter-agent/requirements.txt +0 -9
- jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +0 -68
- jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +0 -91
- jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +0 -91
- jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +0 -57
- jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +0 -53
- jupyter-agent/tmp/jupyter-agent.ipynb +0 -27
- jupyter-agent/utils.py +0 -409
- runbooks/aws/__init__.py +0 -58
- runbooks/aws/dynamodb_operations.py +0 -231
- runbooks/aws/ec2_copy_image_cross-region.py +0 -195
- runbooks/aws/ec2_describe_instances.py +0 -202
- runbooks/aws/ec2_ebs_snapshots_delete.py +0 -186
- runbooks/aws/ec2_run_instances.py +0 -213
- runbooks/aws/ec2_start_stop_instances.py +0 -212
- runbooks/aws/ec2_terminate_instances.py +0 -143
- runbooks/aws/ec2_unused_eips.py +0 -196
- runbooks/aws/ec2_unused_volumes.py +0 -188
- runbooks/aws/s3_create_bucket.py +0 -142
- runbooks/aws/s3_list_buckets.py +0 -152
- runbooks/aws/s3_list_objects.py +0 -156
- runbooks/aws/s3_object_operations.py +0 -183
- runbooks/aws/tagging_lambda_handler.py +0 -183
- runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +0 -619
- runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +0 -738
- runbooks/inventory/aws_organization.png +0 -0
- runbooks/inventory/cfn_move_stack_instances.py +0 -1526
- runbooks/inventory/delete_s3_buckets_objects.py +0 -169
- runbooks/inventory/lockdown_cfn_stackset_role.py +0 -224
- runbooks/inventory/update_aws_actions.py +0 -173
- runbooks/inventory/update_cfn_stacksets.py +0 -1215
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +0 -294
- runbooks/inventory/update_iam_roles_cross_accounts.py +0 -478
- runbooks/inventory/update_s3_public_access_block.py +0 -539
- runbooks/organizations/__init__.py +0 -12
- runbooks/organizations/manager.py +0 -374
- runbooks-0.7.0.dist-info/METADATA +0 -375
- /runbooks/inventory/{tests → Tests}/common_test_data.py +0 -0
- /runbooks/inventory/{tests → Tests}/common_test_functions.py +0 -0
- /runbooks/inventory/{tests → Tests}/script_test_data.py +0 -0
- /runbooks/inventory/{tests → Tests}/setup.py +0 -0
- /runbooks/inventory/{tests → Tests}/src.py +0 -0
- /runbooks/inventory/{tests/test_inventory_modules.py → Tests/test_Inventory_Modules.py} +0 -0
- /runbooks/inventory/{tests → Tests}/test_cfn_describe_stacks.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_ec2_describe_instances.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_lambda_list_functions.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_moto_integration_example.py +0 -0
- /runbooks/inventory/{tests → Tests}/test_org_list_accounts.py +0 -0
- /runbooks/inventory/{Inventory_Modules.py → inventory_modules.py} +0 -0
- /runbooks/{aws → operate}/tags.json +0 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/WHEEL +0 -0
- {runbooks-0.7.0.dist-info → runbooks-0.7.6.dist-info}/licenses/LICENSE +0 -0
@@ -1,169 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
AWS S3 Bucket Object Deletion and Bucket Management Tool
|
4
|
-
|
5
|
-
A specialized utility for safely emptying and optionally deleting S3 buckets,
|
6
|
-
including all object versions and delete markers. Essential for bucket lifecycle
|
7
|
-
management and compliance with data retention policies.
|
8
|
-
|
9
|
-
**AWS API Mapping**:
|
10
|
-
- `boto3.resource('s3').Bucket.object_versions.delete()`
|
11
|
-
- `boto3.resource('s3').Bucket.delete()`
|
12
|
-
|
13
|
-
**SECURITY WARNING**: This script performs DESTRUCTIVE operations:
|
14
|
-
- Permanently deletes ALL objects and versions from specified bucket
|
15
|
-
- Can delete the bucket itself with --force-delete flag
|
16
|
-
- Cannot be undone - ensure proper backups before execution
|
17
|
-
- May affect compliance with data retention requirements
|
18
|
-
|
19
|
-
Features:
|
20
|
-
- Complete object version deletion (including delete markers)
|
21
|
-
- Interactive bucket deletion confirmation
|
22
|
-
- Force deletion mode for automation scenarios
|
23
|
-
- Comprehensive error handling and logging
|
24
|
-
- Single-bucket targeted operation for safety
|
25
|
-
|
26
|
-
Security Controls:
|
27
|
-
- Requires explicit bucket name parameter
|
28
|
-
- Interactive confirmation for bucket deletion
|
29
|
-
- Force flag available for automated scenarios
|
30
|
-
- Detailed logging of all destructive operations
|
31
|
-
|
32
|
-
Compliance Considerations:
|
33
|
-
- Verify data retention policy compliance before execution
|
34
|
-
- Ensure proper backup procedures are in place
|
35
|
-
- Document destruction for audit trails
|
36
|
-
- Consider legal hold and litigation requirements
|
37
|
-
|
38
|
-
Example:
|
39
|
-
Empty bucket but keep it:
|
40
|
-
```bash
|
41
|
-
python delete_s3_buckets_objects.py --profile my-profile --bucket my-bucket
|
42
|
-
```
|
43
|
-
|
44
|
-
Empty and delete bucket with confirmation:
|
45
|
-
```bash
|
46
|
-
python delete_s3_buckets_objects.py --profile my-profile --bucket my-bucket +delete
|
47
|
-
```
|
48
|
-
|
49
|
-
Requirements:
|
50
|
-
- IAM permissions: `s3:DeleteObject`, `s3:DeleteObjectVersion`, `s3:DeleteBucket`
|
51
|
-
- Bucket must be in accessible region
|
52
|
-
- Python 3.8+ with required dependencies
|
53
|
-
|
54
|
-
Author:
|
55
|
-
AWS Cloud Foundations Team
|
56
|
-
|
57
|
-
Version:
|
58
|
-
2023.05.04
|
59
|
-
"""
|
60
|
-
|
61
|
-
import logging
|
62
|
-
|
63
|
-
from account_class import aws_acct_access
|
64
|
-
from ArgumentsClass import CommonArguments
|
65
|
-
|
66
|
-
__version__ = "2023.05.04"
|
67
|
-
|
68
|
-
parser = CommonArguments()
|
69
|
-
parser.singleprofile()
|
70
|
-
parser.singleregion()
|
71
|
-
parser.verbosity()
|
72
|
-
parser.version(__version__)
|
73
|
-
parser.my_parser.add_argument(
|
74
|
-
"-b",
|
75
|
-
"--bucket",
|
76
|
-
dest="pBucketName",
|
77
|
-
metavar="bucket to empty and delete",
|
78
|
-
required=True,
|
79
|
-
help="To specify a bucket, use this parameter.",
|
80
|
-
)
|
81
|
-
parser.my_parser.add_argument(
|
82
|
-
"+delete",
|
83
|
-
"+force-delete",
|
84
|
-
help="Whether or not to delete the bucket after it's been emptied",
|
85
|
-
action="store_const",
|
86
|
-
dest="pForceQuit",
|
87
|
-
const=True,
|
88
|
-
default=False,
|
89
|
-
)
|
90
|
-
args = parser.my_parser.parse_args()
|
91
|
-
|
92
|
-
pProfile = args.Profile
|
93
|
-
pRegion = args.Region
|
94
|
-
pBucketDelete = args.pForceQuit
|
95
|
-
pBucketName = args.pBucketName
|
96
|
-
verbose = args.loglevel
|
97
|
-
logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
98
|
-
|
99
|
-
# Establish AWS session and S3 resource connection
|
100
|
-
# Uses the specified profile to access S3 services
|
101
|
-
aws_acct = aws_acct_access(pProfile)
|
102
|
-
s3 = aws_acct.session.resource(service_name="s3")
|
103
|
-
|
104
|
-
# CRITICAL WARNING: Display destructive operation warning
|
105
|
-
# This ensures users understand the irreversible nature of this operation
|
106
|
-
print()
|
107
|
-
print(f"This script is about to delete all versions of all objects from bucket {pBucketName}")
|
108
|
-
print()
|
109
|
-
|
110
|
-
# Create S3 bucket resource for the specified bucket
|
111
|
-
# This provides access to bucket operations and object management
|
112
|
-
bucket = s3.Bucket(pBucketName)
|
113
|
-
|
114
|
-
try:
|
115
|
-
# DESTRUCTIVE OPERATION: Delete all object versions and delete markers
|
116
|
-
# This includes:
|
117
|
-
# - All current object versions
|
118
|
-
# - All historical object versions (if versioning enabled)
|
119
|
-
# - All delete markers
|
120
|
-
# - Cannot be undone once executed
|
121
|
-
logging.info(f"Starting deletion of all object versions in bucket {pBucketName}")
|
122
|
-
bucket.object_versions.delete()
|
123
|
-
logging.info(f"Successfully deleted all object versions from bucket {pBucketName}")
|
124
|
-
|
125
|
-
except Exception as my_Error:
|
126
|
-
# Handle S3 API errors during object deletion
|
127
|
-
# Common errors: AccessDenied, NoSuchBucket, InvalidBucketState
|
128
|
-
logging.error(f"Failed to delete objects from bucket {pBucketName}: {my_Error}")
|
129
|
-
print(f"Error message: {my_Error}")
|
130
|
-
|
131
|
-
# Handle bucket deletion with safety controls
|
132
|
-
# Provides both automated and interactive deletion modes
|
133
|
-
DeleteBucket = False
|
134
|
-
|
135
|
-
if pBucketDelete:
|
136
|
-
# Force deletion mode: Delete bucket without additional confirmation
|
137
|
-
# Used for automated scenarios where confirmation is handled externally
|
138
|
-
print(f"As per your request, we're deleting the bucket {pBucketName}")
|
139
|
-
logging.warning(f"Force deleting bucket {pBucketName} as requested")
|
140
|
-
|
141
|
-
try:
|
142
|
-
bucket.delete()
|
143
|
-
print(f"Bucket: {pBucketName} has been deleted")
|
144
|
-
logging.info(f"Successfully deleted bucket {pBucketName}")
|
145
|
-
except Exception as delete_error:
|
146
|
-
logging.error(f"Failed to delete bucket {pBucketName}: {delete_error}")
|
147
|
-
print(f"Failed to delete bucket: {delete_error}")
|
148
|
-
|
149
|
-
else:
|
150
|
-
# Interactive deletion mode: Prompt user for confirmation
|
151
|
-
# Provides additional safety control for manual operations
|
152
|
-
DeleteBucket = input("Now that the bucket is empty, do you want to delete the bucket? (y/n): ") in ["y", "Y"]
|
153
|
-
|
154
|
-
if DeleteBucket:
|
155
|
-
try:
|
156
|
-
bucket.delete()
|
157
|
-
print(f"Bucket: {pBucketName} has been deleted")
|
158
|
-
logging.info(f"User confirmed deletion of bucket {pBucketName}")
|
159
|
-
except Exception as delete_error:
|
160
|
-
logging.error(f"Failed to delete bucket {pBucketName}: {delete_error}")
|
161
|
-
print(f"Failed to delete bucket: {delete_error}")
|
162
|
-
else:
|
163
|
-
print(f"Bucket: {pBucketName} has NOT been deleted")
|
164
|
-
logging.info(f"User chose to preserve bucket {pBucketName}")
|
165
|
-
# Operation completion notification
|
166
|
-
print()
|
167
|
-
print("Thanks for using this script...")
|
168
|
-
logging.info("S3 bucket operation completed successfully")
|
169
|
-
print()
|
@@ -1,224 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
import logging
|
4
|
-
import sys
|
5
|
-
|
6
|
-
import boto3
|
7
|
-
import Inventory_Modules
|
8
|
-
import simplejson as json
|
9
|
-
from account_class import aws_acct_access
|
10
|
-
from ArgumentsClass import CommonArguments
|
11
|
-
from botocore.exceptions import ClientError
|
12
|
-
|
13
|
-
__version__ = "2023.05.04"
|
14
|
-
|
15
|
-
parser = CommonArguments()
|
16
|
-
parser.singleprofile()
|
17
|
-
parser.singleregion()
|
18
|
-
parser.verbosity()
|
19
|
-
parser.version(__version__)
|
20
|
-
parser.my_parser.add_argument(
|
21
|
-
"-R",
|
22
|
-
"--access_rolename",
|
23
|
-
dest="pAccessRole",
|
24
|
-
default="AWSCloudFormationStackSetExecutionRole",
|
25
|
-
metavar="role to use for access to child accounts",
|
26
|
-
help="This parameter specifies the role that will allow this script to have access to the children accounts.",
|
27
|
-
)
|
28
|
-
parser.my_parser.add_argument(
|
29
|
-
"-t",
|
30
|
-
"--target_rolename",
|
31
|
-
dest="pTargetRole",
|
32
|
-
default="AWSCloudFormationStackSetExecutionRole",
|
33
|
-
metavar="role to change",
|
34
|
-
help="This parameter specifies the role to have its Trust Policy changed.",
|
35
|
-
)
|
36
|
-
parser.my_parser.add_argument(
|
37
|
-
"+f",
|
38
|
-
"--fix",
|
39
|
-
"+fix",
|
40
|
-
dest="pFix",
|
41
|
-
action="store_const",
|
42
|
-
const=True,
|
43
|
-
default=False,
|
44
|
-
help="This parameter determines whether to make any changes in child accounts.",
|
45
|
-
)
|
46
|
-
parser.my_parser.add_argument(
|
47
|
-
"+l",
|
48
|
-
"--lock",
|
49
|
-
"+lock",
|
50
|
-
dest="pLock",
|
51
|
-
action="store_const",
|
52
|
-
const=True,
|
53
|
-
default=False,
|
54
|
-
help="This parameter determines whether to lock the Trust Policy.",
|
55
|
-
)
|
56
|
-
parser.my_parser.add_argument(
|
57
|
-
"-s",
|
58
|
-
"--safety",
|
59
|
-
dest="pSafety",
|
60
|
-
action="store_const",
|
61
|
-
const=False,
|
62
|
-
default=True,
|
63
|
-
help="Adding this parameter will 'remove the safety' - by not including the principle running this script, which might mean you get locked out of making further changes.",
|
64
|
-
)
|
65
|
-
args = parser.my_parser.parse_args()
|
66
|
-
|
67
|
-
pProfile = args.Profile
|
68
|
-
pTargetRole = args.pTargetRole
|
69
|
-
pAccessRole = args.pAccessRole
|
70
|
-
pLock = args.pLock
|
71
|
-
pSafety = args.pSafety
|
72
|
-
pFix = args.pFix
|
73
|
-
verbose = args.loglevel
|
74
|
-
logging.basicConfig(level=args.loglevel, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
75
|
-
|
76
|
-
aws_acct = aws_acct_access(pProfile)
|
77
|
-
|
78
|
-
if not aws_acct.AccountType.lower() == "root":
|
79
|
-
print()
|
80
|
-
print(f"The profile {pProfile} does not represent an Org")
|
81
|
-
print("This script only works with org accounts.")
|
82
|
-
print()
|
83
|
-
sys.exit(1)
|
84
|
-
##########################
|
85
|
-
ERASE_LINE = "\x1b[2K"
|
86
|
-
##########################
|
87
|
-
|
88
|
-
print(f"We're using the {pAccessRole} role to gain access to the child accounts")
|
89
|
-
print(f"We're targeting the {pTargetRole} role to change its Trust Policy")
|
90
|
-
|
91
|
-
"""
|
92
|
-
1. Collect SSM parameters with the ARNs that should be in the permission
|
93
|
-
2. Create the TrustPolicy in JSON
|
94
|
-
3. Get a listing of all accounts that need to be updated
|
95
|
-
4. Connect to each account, and update the existing trust policy with the new policy
|
96
|
-
"""
|
97
|
-
# 1. Collect parameters with the ARNs that should be in the permission
|
98
|
-
# lock_down_arns_list=[]
|
99
|
-
allowed_arns = []
|
100
|
-
ssm_client = aws_acct.session.client("ssm")
|
101
|
-
param_list = ssm_client.describe_parameters(
|
102
|
-
ParameterFilters=[{"Key": "Name", "Option": "Contains", "Values": ["lock_down_role_arns_list"]}]
|
103
|
-
)["Parameters"]
|
104
|
-
if len(param_list) == 0:
|
105
|
-
print("You need to set the region (-r|--region) to the default region where the SSM parameters are stored.")
|
106
|
-
print("Otherwise, with no *allowed* arns, we would lock everything out from this role.")
|
107
|
-
print("Exiting...")
|
108
|
-
sys.exit(2)
|
109
|
-
for i in param_list:
|
110
|
-
response = param = ssm_client.get_parameter(Name=i["Name"])
|
111
|
-
logging.info(f"Adding {response['Parameter']['Value']} to the list for i: {i['Name']}")
|
112
|
-
allowed_arns.append(response["Parameter"]["Value"])
|
113
|
-
|
114
|
-
# 1.5 Find who is running the script and add their credential as a safety
|
115
|
-
Creds = Inventory_Modules.find_calling_identity(pProfile)
|
116
|
-
if pSafety:
|
117
|
-
allowed_arns.append(Creds["Arn"])
|
118
|
-
# 2. Create the Trust Policy in JSON
|
119
|
-
|
120
|
-
if pLock:
|
121
|
-
if pSafety and pFix:
|
122
|
-
logging.error("Locking down the Trust Policy to *only* the Lambda functions.")
|
123
|
-
elif pFix:
|
124
|
-
logging.error(f"Locking down the Trust Policy to the Lambda functions and {Creds['Arn']}.")
|
125
|
-
else:
|
126
|
-
logging.critical(
|
127
|
-
"While you asked us to lock things down, You didn't use the '+f' parameter, so we're not changing a thing."
|
128
|
-
)
|
129
|
-
Trust_Policy = {
|
130
|
-
"Version": "2012-10-17",
|
131
|
-
"Statement": [
|
132
|
-
{"Sid": "LambdaAccess", "Effect": "Allow", "Principal": {"AWS": allowed_arns}, "Action": "sts:AssumeRole"}
|
133
|
-
],
|
134
|
-
}
|
135
|
-
else:
|
136
|
-
Trust_Policy = {
|
137
|
-
"Version": "2012-10-17",
|
138
|
-
"Statement": [
|
139
|
-
{"Sid": "LambdaAccess", "Effect": "Allow", "Principal": {"AWS": allowed_arns}, "Action": "sts:AssumeRole"},
|
140
|
-
{
|
141
|
-
"Sid": "DevAccess",
|
142
|
-
"Effect": "Allow",
|
143
|
-
"Principal": {"AWS": [f"arn:aws:iam::{aws_acct.MgmtAccount}:root"]},
|
144
|
-
"Action": "sts:AssumeRole",
|
145
|
-
},
|
146
|
-
],
|
147
|
-
}
|
148
|
-
Trust_Policy_json = json.dumps(Trust_Policy)
|
149
|
-
# 3. Get a listing of all accounts that need to be updated and then ...
|
150
|
-
|
151
|
-
|
152
|
-
# 4. Connect to each account, and detach the existing policy, and apply the new policy
|
153
|
-
sts_client = aws_acct.session.client("sts")
|
154
|
-
TrustPoliciesChanged = 0
|
155
|
-
ErroredAccounts = []
|
156
|
-
for acct in aws_acct.ChildAccounts:
|
157
|
-
ConnectionSuccess = False
|
158
|
-
try:
|
159
|
-
role_arn = f"arn:aws:iam::{acct['AccountId']}:role/{pAccessRole}"
|
160
|
-
account_credentials = sts_client.assume_role(RoleArn=role_arn, RoleSessionName="RegistrationScript")[
|
161
|
-
"Credentials"
|
162
|
-
]
|
163
|
-
account_credentials["Account"] = acct["AccountId"]
|
164
|
-
logging.warning(f"Accessed Account {acct['AccountId']} using rolename {pAccessRole}")
|
165
|
-
ConnectionSuccess = True
|
166
|
-
except ClientError as my_Error:
|
167
|
-
logging.error(
|
168
|
-
f"Account {acct['AccountId']}, role {pTargetRole} was unavailable to change, so we couldn't access the role's Trust Policy"
|
169
|
-
)
|
170
|
-
logging.warning(my_Error)
|
171
|
-
ErroredAccounts.append(acct["AccountId"])
|
172
|
-
pass
|
173
|
-
if ConnectionSuccess:
|
174
|
-
try:
|
175
|
-
# detach policy from the role and attach the new policy
|
176
|
-
iam_session = boto3.Session(
|
177
|
-
aws_access_key_id=account_credentials["AccessKeyId"],
|
178
|
-
aws_secret_access_key=account_credentials["SecretAccessKey"],
|
179
|
-
aws_session_token=account_credentials["SessionToken"],
|
180
|
-
)
|
181
|
-
iam_client = iam_session.client("iam")
|
182
|
-
trustpolicyexisting = iam_client.get_role(RoleName=pTargetRole)
|
183
|
-
logging.info(
|
184
|
-
"Found Trust Policy %s in account %s for role %s"
|
185
|
-
% (json.dumps(trustpolicyexisting["Role"]["AssumeRolePolicyDocument"]), acct["AccountId"], pTargetRole)
|
186
|
-
)
|
187
|
-
if pFix:
|
188
|
-
trustpolicyupdate = iam_client.update_assume_role_policy(
|
189
|
-
RoleName=pTargetRole, PolicyDocument=Trust_Policy_json
|
190
|
-
)
|
191
|
-
TrustPoliciesChanged += 1
|
192
|
-
logging.error(f"Updated Trust Policy in Account {acct['AccountId']} for role {pTargetRole}")
|
193
|
-
trustpolicyexisting = iam_client.get_role(RoleName=pTargetRole)
|
194
|
-
logging.info(
|
195
|
-
"Updated Trust Policy %s in account %s for role %s"
|
196
|
-
% (
|
197
|
-
json.dumps(trustpolicyexisting["Role"]["AssumeRolePolicyDocument"]),
|
198
|
-
acct["AccountId"],
|
199
|
-
pTargetRole,
|
200
|
-
)
|
201
|
-
)
|
202
|
-
else:
|
203
|
-
logging.error(f"Account {acct['AccountId']} - no changes made")
|
204
|
-
except ClientError as my_Error:
|
205
|
-
logging.warning(my_Error)
|
206
|
-
pass
|
207
|
-
|
208
|
-
print(ERASE_LINE)
|
209
|
-
print(f"We found {len(aws_acct.ChildAccounts)} accounts under your organization")
|
210
|
-
if pLock and pFix:
|
211
|
-
print(f"We locked {TrustPoliciesChanged} Trust Policies")
|
212
|
-
elif not pLock and pFix:
|
213
|
-
print(f"We unlocked {TrustPoliciesChanged} Trust Policies")
|
214
|
-
else:
|
215
|
-
print(f"We didn't change {TrustPoliciesChanged} Trust Policies")
|
216
|
-
if len(ErroredAccounts) > 0:
|
217
|
-
print(f"We weren't able to access {len(ErroredAccounts)} accounts.")
|
218
|
-
if verbose < 50:
|
219
|
-
print("Here are the accounts that were not updated")
|
220
|
-
for i in ErroredAccounts:
|
221
|
-
print(i)
|
222
|
-
print()
|
223
|
-
print("Thanks for using the tool.")
|
224
|
-
print()
|
@@ -1,173 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import sys
|
3
|
-
from datetime import datetime
|
4
|
-
from os.path import split
|
5
|
-
from queue import Queue
|
6
|
-
from threading import Thread
|
7
|
-
from time import time
|
8
|
-
|
9
|
-
import boto3
|
10
|
-
from ArgumentsClass import CommonArguments
|
11
|
-
from colorama import Fore, Style, init
|
12
|
-
from tqdm.auto import tqdm, trange
|
13
|
-
|
14
|
-
__version__ = "2024.04.24"
|
15
|
-
|
16
|
-
from tqdm import trange
|
17
|
-
|
18
|
-
init()
|
19
|
-
|
20
|
-
|
21
|
-
##################
|
22
|
-
# Functions
|
23
|
-
##################
|
24
|
-
|
25
|
-
|
26
|
-
def parse_args(f_arguments):
|
27
|
-
"""
|
28
|
-
Description: Parses the arguments passed into the script
|
29
|
-
@param f_arguments: args represents the list of arguments passed in
|
30
|
-
@return: returns an object namespace that contains the individualized parameters passed in
|
31
|
-
"""
|
32
|
-
script_path, script_name = split(sys.argv[0])
|
33
|
-
parser = CommonArguments()
|
34
|
-
parser.my_parser.description = "We're going to find all API actions within the available AWS services."
|
35
|
-
parser.timing()
|
36
|
-
parser.save_to_file()
|
37
|
-
parser.verbosity()
|
38
|
-
parser.version(__version__)
|
39
|
-
local = parser.my_parser.add_argument_group(script_name, "Parameters specific to this script")
|
40
|
-
# local.add_argument(
|
41
|
-
# "-s", "--status",
|
42
|
-
# dest="pStatus",
|
43
|
-
# choices=['running', 'stopped'],
|
44
|
-
# type=str,
|
45
|
-
# default=None,
|
46
|
-
# help="Whether you want to limit the instances returned to either 'running', 'stopped'. Default is both")
|
47
|
-
return parser.my_parser.parse_args(f_arguments)
|
48
|
-
|
49
|
-
|
50
|
-
def random_string(stringLength=10):
|
51
|
-
"""
|
52
|
-
Description: Generates a random string
|
53
|
-
@param stringLength: to determine the length of the random number generated
|
54
|
-
@return: returns a random string of characters of length "stringlength"
|
55
|
-
"""
|
56
|
-
import random
|
57
|
-
import string
|
58
|
-
|
59
|
-
# Generate a random string of fixed length
|
60
|
-
letters = string.ascii_lowercase
|
61
|
-
randomstring = "".join(random.choice(letters) for _ in range(stringLength))
|
62
|
-
return randomstring
|
63
|
-
|
64
|
-
|
65
|
-
def get_aws_actions():
|
66
|
-
"""
|
67
|
-
Description: Gets all the actions for all the services in AWS
|
68
|
-
@return: list of actions
|
69
|
-
"""
|
70
|
-
|
71
|
-
class AWSActionThread(Thread):
|
72
|
-
def __init__(self, queue):
|
73
|
-
Thread.__init__(self)
|
74
|
-
self.queue = queue
|
75
|
-
|
76
|
-
def run(self):
|
77
|
-
while True:
|
78
|
-
# existing code to get actions for a service
|
79
|
-
c_service = self.queue.get()
|
80
|
-
client = boto3.client(c_service)
|
81
|
-
try:
|
82
|
-
list_of_operations = client.meta.method_to_api_mapping.keys()
|
83
|
-
# print(f"{ERASE_LINE}Checking actions for {c_service}", end='\r', flush=True)
|
84
|
-
for operation in tqdm(list_of_operations, desc=f"actions for {c_service}", leave=False):
|
85
|
-
action_list.append({"Service": c_service, "Operation": operation})
|
86
|
-
except AttributeError as myError:
|
87
|
-
print(myError)
|
88
|
-
action_list.append({"Service": c_service, "Operation": None})
|
89
|
-
pass
|
90
|
-
finally:
|
91
|
-
self.queue.task_done()
|
92
|
-
|
93
|
-
checkqueue = Queue()
|
94
|
-
action_list = []
|
95
|
-
workerthreads = 10
|
96
|
-
|
97
|
-
for x in trange(
|
98
|
-
workerthreads,
|
99
|
-
desc=f"Creating {workerthreads} worker threads",
|
100
|
-
leave=False,
|
101
|
-
# , position=0
|
102
|
-
):
|
103
|
-
worker = AWSActionThread(checkqueue)
|
104
|
-
worker.daemon = True
|
105
|
-
worker.start()
|
106
|
-
|
107
|
-
# Create a thread for every service.
|
108
|
-
print(f"Capturing all available AWS Services...")
|
109
|
-
for service in tqdm(
|
110
|
-
list_of_services,
|
111
|
-
desc=f"Checking actions for each service",
|
112
|
-
# , position=0
|
113
|
-
):
|
114
|
-
# for service in list_of_services:
|
115
|
-
checkqueue.put(service)
|
116
|
-
checkqueue.join()
|
117
|
-
|
118
|
-
return action_list
|
119
|
-
|
120
|
-
|
121
|
-
def save_file(file_to_save_to: str = None, input_data: list = None):
|
122
|
-
"""
|
123
|
-
Description: Saves the data to a file
|
124
|
-
@param file_to_save_to:
|
125
|
-
@param input_data:
|
126
|
-
@return: None
|
127
|
-
"""
|
128
|
-
if input_data is None:
|
129
|
-
print(f"Input data cannot be none. Exiting...")
|
130
|
-
raise SystemExit(1)
|
131
|
-
elif len(input_data) == 0:
|
132
|
-
print(f"No data to save. Exiting...")
|
133
|
-
raise SystemExit(1)
|
134
|
-
|
135
|
-
if file_to_save_to is None:
|
136
|
-
print(f"No filename provided to save data.\nUsing a randomized name.")
|
137
|
-
file_to_save_to = random_string(15) + ".txt"
|
138
|
-
else:
|
139
|
-
logging.info(f"Saving data to {file_to_save_to}")
|
140
|
-
with open(file_to_save_to, "w", encoding="utf-8") as f:
|
141
|
-
for item in input_data:
|
142
|
-
f.write(f"{item['Service']}:{item['Operation']}\n")
|
143
|
-
return file_to_save_to
|
144
|
-
|
145
|
-
|
146
|
-
##################
|
147
|
-
# Main
|
148
|
-
##################
|
149
|
-
ERASE_LINE = "\x1b[2K"
|
150
|
-
begin_time = time()
|
151
|
-
|
152
|
-
if __name__ == "__main__":
|
153
|
-
arguments = parse_args(sys.argv[1:])
|
154
|
-
pTiming = arguments.Time
|
155
|
-
file_to_save = arguments.Filename
|
156
|
-
verbose = arguments.loglevel
|
157
|
-
logging.basicConfig(level=verbose, format="[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s")
|
158
|
-
logging.getLogger("boto3").setLevel(logging.CRITICAL)
|
159
|
-
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
160
|
-
logging.getLogger("s3transfer").setLevel(logging.CRITICAL)
|
161
|
-
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
|
162
|
-
|
163
|
-
list_of_services = boto3.Session().get_available_services()
|
164
|
-
all_actions = get_aws_actions()
|
165
|
-
if pTiming:
|
166
|
-
print(ERASE_LINE)
|
167
|
-
print(f"{Fore.GREEN}This script took {time() - begin_time:.2f} seconds{Fore.RESET}")
|
168
|
-
print(ERASE_LINE)
|
169
|
-
print(f"Found {len(all_actions)} actions across {len(list_of_services)} services")
|
170
|
-
filename = save_file(file_to_save, all_actions)
|
171
|
-
print(f"Saved to {filename}")
|
172
|
-
print("Thank you for using this script")
|
173
|
-
print()
|