Skip to content

Commit e75a114

Browse files
Create configure-forensic (#122)
Helper file for customers
1 parent a7010e7 commit e75a114

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import boto3
2+
import json
3+
import os
4+
import re
5+
import argparse
6+
from botocore.exceptions import ClientError
7+
# -------------------------------
8+
# Usage:
9+
#
10+
# python configure_forensics.py --bucket <bucket-name>
11+
# -------------------------------
12+
13+
14+
# -------------------------------
15+
# Configuration
16+
# -------------------------------
17+
ROLE_PREFIXES = ["elastio-account-level-stack-", "elastio-account-level-sta-"]
18+
ROLE_SUFFIXES = [
19+
"awsS3ScanBgJob", "awsFsxOntapScanBgJob", "awsEfsScanBgJob", "awsEc2ScanBgJob",
20+
"awsEbsSnapshotsScanBgJob", "awsEbsScanBgJob", "awsDrsSnapshotScanBgJob",
21+
"awsBackupRpVmScanBgJob", "awsBackupRpS3ScanBgJob", "awsBackupRpIscanBgJob",
22+
"awsBackupRpEfsScanBgJob", "awsBackupRpEc2ScanBgJob", "awsBackupRpEbsScanBgJob"
23+
]
24+
POLICY_FILENAME = "policy.json"
25+
26+
# -------------------------------
27+
# Helpers
28+
# -------------------------------
29+
def get_account_id():
30+
return boto3.client("sts").get_caller_identity()["Account"]
31+
32+
def get_verified_roles(account_id):
33+
iam = boto3.client('iam')
34+
paginator = iam.get_paginator('list_roles')
35+
valid_arns = set()
36+
37+
print(f"🔍 Scanning IAM roles in account {account_id}...\n")
38+
for page in paginator.paginate():
39+
for role in page['Roles']:
40+
role_name = role['RoleName']
41+
for prefix in ROLE_PREFIXES:
42+
for suffix in ROLE_SUFFIXES:
43+
if role_name.startswith(prefix + suffix):
44+
try:
45+
iam.get_role(RoleName=role_name)
46+
arn = f"arn:aws:iam::{account_id}:role/{role_name}"
47+
valid_arns.add(arn)
48+
print(f"✔ Valid role: {arn}")
49+
except iam.exceptions.NoSuchEntityException:
50+
print(f"✘ Skipped missing role: {role_name}")
51+
except Exception as e:
52+
print(f"⚠ Error checking {role_name}: {e}")
53+
break
54+
return sorted(valid_arns)
55+
56+
def generate_policy(bucket_name, role_arns):
57+
return {
58+
"Version": "2012-10-17",
59+
"Statement": [
60+
{
61+
"Effect": "Allow",
62+
"Principal": { "AWS": role_arns },
63+
"Action": ["s3:PutObject", "s3:PutObjectTagging"],
64+
"Resource": f"arn:aws:s3:::{bucket_name}/*"
65+
},
66+
{
67+
"Effect": "Allow",
68+
"Principal": { "AWS": role_arns },
69+
"Action": "s3:ListBucket",
70+
"Resource": f"arn:aws:s3:::{bucket_name}"
71+
}
72+
]
73+
}
74+
75+
def save_policy_to_file(policy):
76+
with open(POLICY_FILENAME, "w") as f:
77+
json.dump(policy, f, indent=4)
78+
print(f"\n📁 Policy written to: {os.path.abspath(POLICY_FILENAME)}")
79+
80+
def update_batch_jobs(bucket_name):
81+
env_vars_to_add = [
82+
{'name': 'ELASTIO_FORENSIC_ANALYSIS_ARCHIVAL_ENABLED', 'value': 'TRUE'},
83+
{'name': 'ELASTIO_FORENSIC_ANALYSIS_ARCHIVAL_S3_BUCKET_NAME', 'value': bucket_name}
84+
]
85+
86+
ec2 = boto3.client('ec2')
87+
regions = [r['RegionName'] for r in ec2.describe_regions()['Regions']]
88+
print(f"\n🌐 Found {len(regions)} AWS regions.")
89+
90+
for region in regions:
91+
print(f"\n🌍 Checking region: {region}")
92+
batch = boto3.client('batch', region_name=region)
93+
94+
try:
95+
response = batch.describe_job_definitions(status='ACTIVE')
96+
job_definitions = response['jobDefinitions']
97+
except ClientError as e:
98+
print(f"⚠ Skipping region {region} due to error: {e}")
99+
continue
100+
101+
if not job_definitions:
102+
print("No active job definitions found.")
103+
continue
104+
105+
for job_def in job_definitions:
106+
job_name = job_def['jobDefinitionName']
107+
revision = job_def['revision']
108+
109+
if job_name.startswith("elastio"):
110+
print(f"🔧 Updating job: {job_name}:{revision}")
111+
112+
container_props = job_def['containerProperties']
113+
existing_env = container_props.get('environment', [])
114+
115+
env_dict = {env['name']: env['value'] for env in existing_env}
116+
for new_env in env_vars_to_add:
117+
env_dict[new_env['name']] = new_env['value']
118+
updated_env = [{'name': k, 'value': v} for k, v in env_dict.items()]
119+
120+
sanitized_props = {
121+
k: v for k, v in container_props.items() if k != 'networkConfiguration'
122+
}
123+
sanitized_props['environment'] = updated_env
124+
125+
try:
126+
response = batch.register_job_definition(
127+
jobDefinitionName=job_name,
128+
type=job_def['type'],
129+
containerProperties=sanitized_props
130+
)
131+
print(f"✅ Registered new revision: {response['jobDefinitionArn']}")
132+
except ClientError as e:
133+
print(f"❌ Failed to register {job_name} in {region}: {e}")
134+
135+
print("\n🎉 Multi-region job update completed.")
136+
137+
# -------------------------------
138+
# Entry Point
139+
# -------------------------------
140+
if __name__ == "__main__":
141+
parser = argparse.ArgumentParser(description="Configure S3 bucket policy and enable forensic archival.")
142+
parser.add_argument("--bucket", type=str, default="elastio-forensic", help="S3 bucket name")
143+
args = parser.parse_args()
144+
bucket_name = args.bucket
145+
146+
print(f"\n🚀 Starting configuration for bucket: {bucket_name}")
147+
148+
account_id = get_account_id()
149+
role_arns = get_verified_roles(account_id)
150+
151+
if not role_arns:
152+
print("❌ No valid roles found. Exiting.")
153+
else:
154+
policy = generate_policy(bucket_name, role_arns)
155+
print("\n✅ Generated policy:\n")
156+
print(json.dumps(policy, indent=4))
157+
save_policy_to_file(policy)
158+
159+
update_batch_jobs(bucket_name)

0 commit comments

Comments
 (0)