UPD: Updated AwsEnvReview.py and showing instance name in the report

This commit is contained in:
xpk
2025-02-27 09:25:02 +08:00
parent ad638bb6fb
commit f039deada8
2 changed files with 124 additions and 577 deletions
+124 -94
View File
@@ -14,18 +14,14 @@ import jmespath
import re
from pprint import pprint
from datetime import date
from mdutils.mdutils import MdUtils
def printTitle(title):
print("\n")
print("=" * len(title))
print(title.upper())
print("=" * len(title))
return
def printSubTitle(title):
print("\n" + title + "\n")
def printTitle(level: int, title: str):
if level <= 2:
mdFile.new_header(level=level, title=title)
else:
mdFile.new_paragraph(title)
return
@@ -40,26 +36,33 @@ def getAgeFromDate(inputDate):
def printResult(content: list, header: str):
header = "Index, " + header
if len(content) <= 0:
print("👍 No issue found.")
else:
print(header)
print("-" * len(header))
for count, row in enumerate(content):
print(count+1, *row, sep=", ")
mdFile.new_paragraph("👏 No issue found.")
return
header = "Item," + header
table = header.split(",")
tableCol = len(table)
for count, row in enumerate(content):
row.insert(0, count+1)
table.extend(row)
mdFile.new_line()
mdFile.new_table(columns=tableCol, rows=len(content)+1, text=table, text_align='left')
return
print("Script started. It may take 7+ minutes to run. Report will be saved to AwsReviewReport.md. Please be patient...")
mdFile = MdUtils(file_name='AwsReviewReport.md', title='Aws Review ' + str(date.today()))
sts = boto3.client("sts")
aid = sts.get_caller_identity().get("Account")
client = boto3.client('ec2', region_name="us-east-1")
regions = getAllRegions(client)
print("AWS Environment Review - " + str(date.today()) + "\n\n")
mdFile.write("-" * 5)
printTitle("Ec2 service review")
printSubTitle("[Cost Optimization] Instances stopped for over 14 days - Consider backing up and terminate instances "
printTitle(1, "Ec2 service review")
printTitle(2, "[Cost Optimization] Instances stopped for over 14 days")
printTitle(3, "Consider backing up and terminate instances "
"or use AutoScalingGroup to spin up and down instances as needed.")
outTable = []
@@ -72,7 +75,8 @@ for r in regions:
outTable.append([r, aid, i[0].get("InstanceId"), getAgeFromDate(i[0].get("UsageOperationUpdateTime"))])
printResult(outTable, "Region, AccountID, InstanceId, DaysStopped")
printSubTitle("[Security] Insecure IDMSv1 allowed - Consider requiring IDMSv2. For more information, "
printTitle(2, "[Security] Insecure IDMSv1 allowed")
printTitle(3, "Consider requiring IDMSv2. For more information, "
"see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html")
outTable = []
@@ -82,11 +86,16 @@ for r in regions:
if len(response.get("Reservations")) > 0:
for i in jmespath.search("Reservations[*].Instances[*]", response):
if i[0].get("MetadataOptions").get("HttpTokens") == "optional":
outTable.append([r, aid, i[0].get("InstanceId"), i[0].get("MetadataOptions").get("HttpTokens") ])
outTable.append([
r,
aid,
i[0].get("InstanceId"),
i[0].get("MetadataOptions").get("HttpTokens")
])
printResult(outTable, "Region, AccountID, InstanceId, IDMSv2")
printSubTitle("[Sustainability] Use of previous generation instance type - "
"Consider using current generation instances")
printTitle(2,"[Sustainability] Use of early generation instance type")
printTitle(3, "Consider using current generation instances")
outTable = []
for r in regions:
client = boto3.client('ec2', region_name=r)
@@ -94,10 +103,17 @@ for r in regions:
if len(response.get("Reservations")) > 0:
for i in jmespath.search("Reservations[*].Instances[*]", response):
if re.search("^(t1|t2|m3|m1|m2|m4|c1|c2|c3|c4|r3|r4|i2)", i[0].get("InstanceType")) is not None:
outTable.append([r, aid, i[0].get("InstanceId"), i[0].get("InstanceType")])
printResult(outTable, "Region, AccountID, InstanceId, InstanceType")
outTable.append([
r,
aid,
i[0].get("InstanceId"),
jmespath.search("Tags[?Key =='Name'].Value", i[0])[0],
i[0].get("InstanceType")
])
printResult(outTable, "Region, AccountID, InstanceId, InstanceName, InstanceType")
printSubTitle("[Cost Optimization] Unattached EBS volumes - Consider backing up the volumes and delete them")
printTitle(2, "[Cost Optimization] Unattached EBS volumes")
printTitle(3, "Consider backing up the volumes and delete them")
outTable = []
for r in regions:
client = boto3.client('ec2', region_name=r)
@@ -113,8 +129,8 @@ for r in regions:
outTable.append([r, aid, i.get("VolumeId"), i.get("Size"), i.get("VolumeType")])
printResult(outTable, "Region, AccountID, VolumeId, Size, VolumeType")
printSubTitle("[Cost Optimization] EBS snapshots more than 365 days old - "
"Consider removing snapshots if no longer needed")
printTitle(2, "[Cost Optimization] EBS snapshots more than 365 days old")
printTitle(3,"Consider removing snapshots if no longer needed")
outTable = []
for r in regions:
client = boto3.client('ec2', region_name=r)
@@ -122,12 +138,14 @@ for r in regions:
OwnerIds=[aid]
)
for i in response.get("Snapshots"):
if getAgeFromDate(i.get("StartTime")) > 365 and i.get("Description") != "This snapshot is created by the AWS Backup service.":
outTable.append([r, aid, i.get("SnapshotId"), i.get("Description")[:70], getAgeFromDate(i.get("StartTime"))])
if getAgeFromDate(i.get("StartTime")) > 365 and i.get(
"Description") != "This snapshot is created by the AWS Backup service.":
outTable.append(
[r, aid, i.get("SnapshotId"), i.get("Description")[:70], getAgeFromDate(i.get("StartTime"))])
printResult(outTable, "Region, AccountID, SnapshotId, Description, SnapshotAge")
printSubTitle("[Security] Unencrypted EBS volumes - Consider replacing volume with encrypted ones. "
printTitle(2, "[Security] Unencrypted EBS volumes")
printTitle(3, "Consider replacing volume with encrypted ones. "
"One can do so by stopping the Ec2 instance, creating snapshot for the unencrypted volume, "
"copy the snapshot to a new encrypted snapshot, create a volume from the encrypted snapshot,"
"detach the original volume and attach the encrypted volume. Remember to clean up the volumes"
@@ -151,8 +169,8 @@ for r in regions:
outTable.append([r, aid, i.get("VolumeId"), i.get("Size"), i.get("VolumeType")])
printResult(outTable, "Region, AccountID, VolumeId, Size, VolumeType")
printSubTitle("[Cost Optimization] Unused Elastic IP - Consider deleting unused EIP")
printTitle(2, "[Cost Optimization] Unused Elastic IP")
printTitle(3, "Consider deleting unused EIP")
outTable = []
for r in regions:
@@ -163,9 +181,9 @@ for r in regions:
outTable.append([r, aid, i.get("PublicIp")])
printResult(outTable, "Region, AccountID, PublicIp")
printTitle("Security group review")
printSubTitle("[Security] Security group rules allowing ingress from 0.0.0.0/0 - Consider setting more restrictive rules "
"allowing access from specific sources.")
printTitle(1, "Security group review")
printTitle(2, "[Security] Security group rules allowing ingress from 0.0.0.0/0")
printTitle(3, "Consider setting more restrictive rules allowing access from specific sources.")
outTable = []
for r in regions:
@@ -178,11 +196,13 @@ for r in regions:
and sgr.get("ToPort") != 443
and sgr.get("FromPort") != 80
and sgr.get("ToPort") != 80):
outTable.append([r, aid, sgr.get("GroupId"), sgr.get("SecurityGroupRuleId"), sgr.get("FromPort"), sgr.get("ToPort")])
outTable.append(
[r, aid, sgr.get("GroupId"), sgr.get("SecurityGroupRuleId"), sgr.get("FromPort"), sgr.get("ToPort")])
printResult(outTable, "Region, AccountID, SecurityGroup, Rule, FromPort, ToPort")
printTitle("Rds service review")
printSubTitle("[Security] Unencrypted RDS instances - Consider encrypting RDS instances. For more detail, see "
printTitle(1, "Rds service review")
printTitle(2, "[Security] Unencrypted RDS instances")
printTitle(3, "Consider encrypting RDS instances. For more detail, see "
"https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/encrypt-an-existing-amazon-rds-for-postgresql-db-instance.html")
outTable = []
for r in regions:
@@ -197,9 +217,8 @@ for r in regions:
outTable.append([r, aid, i.get("DBClusterIdentifier"), i.get("Engine")])
printResult(outTable, "Region, AccountID, DBIdentifier, Engine")
printSubTitle("[Reliability] RDS instance running in single availability zone - "
"Consider enabling multi-az for production use.")
printTitle(2, "[Reliability] RDS instance running in single availability zone")
printTitle(3, "Consider enabling multi-az for production use.")
outTable = []
for r in regions:
client = boto3.client('rds', region_name=r)
@@ -213,9 +232,9 @@ for r in regions:
outTable.append([r, aid, i.get("DBClusterIdentifier"), i.get("Engine")])
printResult(outTable, "Region, AccountID, DBIdentifier, Engine")
printTitle("Lambda service review")
printSubTitle("[Security] Outdated Lambda runtime - Consider changing to currently supported Lambda runtime versions, "
printTitle(1, "Lambda service review")
printTitle(2, "[Security] Outdated Lambda runtime")
printTitle(3, "Consider changing to currently supported Lambda runtime versions, "
"listed on https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html")
outTable = []
for r in regions:
@@ -227,9 +246,9 @@ for r in regions:
outTable.append([r, aid, i.get("FunctionName"), i.get("Runtime")])
printResult(outTable, "Region, AccountID, FunctionName, Runtime")
printTitle("Iam service review")
printSubTitle("[Security] Iam user access key not rotated for 180 days - Consider rotating access key")
printTitle(1, "Iam service review")
printTitle(2, "[Security] Iam user access key not rotated for 180 days")
printTitle(3, "Consider rotating access key")
outTable = []
client = boto3.client('iam', region_name="us-east-1")
@@ -242,9 +261,10 @@ for u in users:
outTable.append([aid, u, i.get("AccessKeyId"), getAgeFromDate(i.get("CreateDate"))])
printResult(outTable, "AccountID, UserName, AccessKeyId, AccessKeyAge")
printSubTitle("[Security] Iam AdministratorAccess policy attached - Consider granting minimum privileges "
"to users/groups/roles. AWS managed policies for job functions are recommended. See "
"https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html")
printTitle(2, "[Security] Iam AdministratorAccess policy attached")
printTitle(3, "Consider granting minimum privileges "
"to users/groups/roles. AWS managed policies for job functions are recommended. See "
"https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html")
outTable = []
client = boto3.client('iam', region_name="us-east-1")
@@ -259,9 +279,9 @@ for role in jmespath.search("PolicyRoles[*].RoleName", entityResp):
outTable.append([aid, "Role", role])
printResult(outTable, "AccountID, Type, Name")
printTitle("Cloudwatch service review")
printSubTitle("[Cost Optimization] Cloudwatch LogGroups without retention period - Consider setting retention")
printTitle(1, "Cloudwatch service review")
printTitle(2, "[Cost Optimization] Cloudwatch LogGroups without retention period")
printTitle(3, "Consider setting retention")
outTable = []
for r in regions:
@@ -269,10 +289,11 @@ for r in regions:
response = client.describe_log_groups()
for i in response.get("logGroups"):
if i.get("retentionInDays") is None:
outTable.append([r, aid, i.get("logGroupName"), int(round(i.get("storedBytes")/1024/1024,0))])
outTable.append([r, aid, i.get("logGroupName"), int(round(i.get("storedBytes") / 1024 / 1024, 0))])
printResult(outTable, "Region, AccountID, LogGroup, SizeMiB")
printSubTitle("[Security] Cloudwatch LogGroups unencrypted - Consider encrypting LogGroups")
printTitle(2, "[Security] Cloudwatch LogGroups unencrypted")
printTitle(3, "Consider encrypting LogGroups")
outTable = []
for r in regions:
@@ -283,9 +304,9 @@ for r in regions:
outTable.append([r, aid, i.get("logGroupName")])
printResult(outTable, "Region, AccountID, LogGroup")
printTitle("Backup service review")
printSubTitle("[Reliability] Ec2/Rds instances found but AWSBackup plan missing - "
"Consider setting up AWSBackup plans to backup AWS resources.")
printTitle(1, "Backup service review")
printTitle(2, "[Reliability] Ec2/Rds instances found but AWSBackup plan missing")
printTitle(3, "Consider setting up AWSBackup plans to backup AWS resources.")
outTable = []
for r in regions:
client = boto3.client('backup', region_name=r)
@@ -302,8 +323,9 @@ for r in regions:
outTable.append([r, aid, "AWSBackup plan missing", instanceCount])
printResult(outTable, "Region, AccountID, BackupPlan, Ec2RdsInstances")
printTitle("S3 service review")
printSubTitle("[Security] S3 bucket policy missing - Consider creating bucket policy and restrict access to bucket")
printTitle(1, "S3 service review")
printTitle(2, "[Security] S3 bucket policy missing")
printTitle(3, "Consider creating bucket policy and restrict access to bucket")
outTable = []
client = boto3.client('s3', region_name="us-east-1")
@@ -315,9 +337,9 @@ for i in jmespath.search("Buckets[*].Name", response):
outTable.append([aid, i])
printResult(outTable, "AccountID, BucketName")
printTitle("ElastiCache review")
printSubTitle("[Sustainability] ElastiCache instances on x64 platform - Consider Graviton instances "
"such as t4g/r7g to optimize your infrastructure investment.")
printTitle(1, "ElastiCache review")
printTitle(2, "[Sustainability] ElastiCache instances on x64 platform")
printTitle(3, "Consider Graviton instances such as t4g/r7g to optimize your infrastructure investment.")
outTable = []
for r in regions:
@@ -328,8 +350,9 @@ for r in regions:
outTable.append([r, aid, i.get("CacheClusterId"), i.get("CacheNodeType")])
printResult(outTable, "Region, AccountID, CacheClusterId, CacheNodeType")
printTitle("LoadBalancer service review")
printSubTitle("[Cost Optimization] LB Target group without targets - Consider removing empty target groups")
printTitle(1, "LoadBalancer service review")
printTitle(2, "[Cost Optimization] LB Target group without targets")
printTitle(3, "Consider removing empty target groups")
outTable = []
for r in regions:
@@ -341,9 +364,9 @@ for r in regions:
outTable.append([r, aid, i.get("TargetGroupName")])
printResult(outTable, "Region, AccountID, TargetGroup")
printTitle("KMS service review")
printSubTitle("[Security] Customer Managed Keys do not have auto rotation enabled - "
"Consider enabling auto key rotation. When a key is rotated, previous ones "
printTitle(1, "KMS service review")
printTitle(2, "[Security] Customer Managed Keys do not have auto rotation enabled")
printTitle(3, "Consider enabling auto key rotation. When a key is rotated, previous ones "
"are still kept within AWS to allow data retrival.")
outTable = []
@@ -362,8 +385,9 @@ for r in regions:
pass
printResult(outTable, "Region, AccountID, KeyId")
printTitle("ApiGateway service review")
printSubTitle("[Security] ApiGateway resource policy missing - Consider restricting access to private API with a "
printTitle(1, "ApiGateway service review")
printTitle(2, "[Security] ApiGateway resource policy missing")
printTitle(3, "Consider restricting access to private API with a "
"policy. Private Api should be accessed through Vpc endpoint and a policy ensures the Api cannot "
"be accessed otherwise. For more detail, see "
"https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-examples.html")
@@ -377,20 +401,23 @@ for r in regions:
outTable.append([r, aid, i.get("name")])
printResult(outTable, "Region, AccountID, PrivateApiName")
printTitle("Cloudtrail service review")
printSubTitle("[Security] Cloudtrail not encrypted - Consider enabling encryption for cloudtrail")
"""Check cloudtrail for encryption"""
printTitle(1, "Cloudtrail service review")
printTitle(2, "[Security] Cloudtrail not encrypted")
printTitle(3, "Consider enabling encryption for cloudtrail")
outTable = []
for r in regions:
client = boto3.client('cloudtrail', region_name=r)
response = client.describe_trails()
for i in response.get("trailList"):
if i.get("KmsKeyId") is None:
outTable.append([r, aid, i.get("Name")])
printResult(outTable, "Region, AccountID, Trail")
#for r in regions:
client = boto3.client('cloudtrail')
response = client.describe_trails()
for i in response.get("trailList"):
if i.get("KmsKeyId") is None:
outTable.append([aid, i.get("TrailARN")])
printResult(outTable, "AccountID, Trail")
printSubTitle("[Security] Multi-Region cloudtrail not enabled - Consider enabling Multi-Region for at least 1 cloudtrail")
"""Check cloudtrail for multi-region logging"""
printTitle(2, "[Security] Multi-Region cloudtrail not enabled")
printTitle(3, "Consider enabling Multi-Region for at least 1 cloudtrail")
outTable = []
multiRegionTrailCount = 0
@@ -405,9 +432,9 @@ for r in regions:
outTable.append([r, aid, "Missing multi region trail"])
printResult(outTable, "Region, AccountID, Status")
printTitle("Vpc service review")
printSubTitle("[Reliability] Insufficient VPN tunnels - Consider having 2 tunnels for each site VPN connection. "
printTitle(1, "Vpc service review")
printTitle(2, "[Reliability] Insufficient VPN tunnels")
printTitle(3, "Consider having 2 tunnels for each site VPN connection. "
"AWS performs VPN tunnel endpoint maintenance rather frequently. Having 2 tunnel reduces the risk "
"of service interruption.")
outTable = []
@@ -417,12 +444,13 @@ for r in regions:
response = client.describe_vpn_connections()
for i in response.get("VpnConnections"):
if len(jmespath.search("Options.TunnelOptions[*].OutsideIpAddress", i)) < 2:
outTable.append([r, aid, i.get("VpnConnectionId"), len(jmespath.search("Options.TunnelOptions[*].OutsideIpAddress", i))])
outTable.append([r, aid, i.get("VpnConnectionId"),
len(jmespath.search("Options.TunnelOptions[*].OutsideIpAddress", i))])
printResult(outTable, "Region, AccountID, VpnConnection, TunnelCount")
printTitle("Eks service review")
printSubTitle("[Sustainability] Eks node running on AmazonLinux2 (AL2) - Consider using AmazonLinux2023. "
printTitle(1, "Eks service review")
printTitle(2, "[Sustainability] Eks node running on AmazonLinux2 (AL2)")
printTitle(3, "Consider using AmazonLinux2023. "
"AL2's end of life date is 2025-06-30. AmazonLinux2023 runs on newer kernel and libraries, "
"which offers better performance and security.")
outTable = []
@@ -441,7 +469,8 @@ for r in regions:
outTable.append([r, aid, cluster, ng, ngResp.get("nodegroup").get("amiType")])
printResult(outTable, "Region, AccountID, Cluster, NodeGroup, AmiType")
printSubTitle("[Sustainability] Eks control plane version outdated - Consider using upgrading Eks cluster. "
printTitle(2, "[Sustainability] Eks control plane version outdated")
printTitle(3, "Consider using upgrading Eks cluster. "
"Reference https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html for a list "
"of current versions. Reference https://docs.aws.amazon.com/eks/latest/userguide/update-cluster.html "
"for upgrade instructions.")
@@ -456,9 +485,10 @@ for r in regions:
outTable.append([r, aid, cluster, clusterResp.get("cluster").get("version")])
printResult(outTable, "Region, AccountID, Cluster, Version")
mdFile.create_md_file()
print("Report written to AwsReviewReport.md")
# TODO
"""
- config enabled for all regions
- list users/groups/roles with administrator access
"""
"""