diff --git a/README.md b/README.md
index 8919e42..b09d65d 100755
--- a/README.md
+++ b/README.md
@@ -28,7 +28,11 @@ Running this tool is free as it is covered under the AWS Free Tier. If you have
1. [Log in to your AWS account](https://docs.aws.amazon.com/cloudshell/latest/userguide/getting-started.html#start-session) using the IAM User with sufficient permissions described above.
2. Launch [AWS CloudShell](https://docs.aws.amazon.com/cloudshell/latest/userguide/getting-started.html#launch-region-shell) in any region.
+
+Launch AWS Cloudshell Walkthrough
+
![Launch AWS CloudShell](https://d39bs20xyg7k53.cloudfront.net/services-screener/p1-cloudshell.gif)
+
In the AWS CloudShell terminal, run this script this to install the dependencies:
```bash
@@ -42,8 +46,11 @@ pip install -r requirements.txt
alias screener="python3 $(pwd)/main.py"
```
-
+
+Install Dependecies Walkthrough
+
![Install dependencies](https://d39bs20xyg7k53.cloudfront.net/services-screener/p2-dependencies.gif)
+
## Using Service Screener
When running Service Screener, you will need to specify the regions and services you would like it to run on. It currently supports Amazon Cloudfront, AWS Cloudtrail, Amazon Dynamodb, Amazon EC2, Amazon EFS, Amazon RDS, Amazon EKS, Amazon Elasticache, Amazon Guardduty, AWS IAM, Amazon Opensearch, AWS Lambda, and Amazon S3.
@@ -72,7 +79,6 @@ screener --regions ap-southeast-1,us-east-1 --services rds,iam
**Example 5: Run in the Singapore region, filter resources based on tags (e.g: Name=env Values=prod and Name=department Values=hr,coe)**
```
-## NOT SUPPORTED YET, TO BE RELEASED SOON
screener --regions ap-southeast-1 --filters env=prod%department=hr,coe
```
@@ -90,12 +96,19 @@ screener --regions ALL
# api-raw: raw findings
# report: generate default web html
```
+
+Get Report Walkthrough
+
![Get Report](https://d39bs20xyg7k53.cloudfront.net/services-screener/p3-getreport.gif)
+
### Downloading the report
The output is generated as a ~/service-screener-v2/output.zip file.
You can [download the file](https://docs.aws.amazon.com/cloudshell/latest/userguide/working-with-cloudshell.html#files-storage) in the CloudShell console by clicking the *Download file* button under the *Actions* menu on the top right of the Cloudshell console.
+
+Download Output & Report Viewing Walkthrough
+
![Download Output](https://d39bs20xyg7k53.cloudfront.net/services-screener/p4-outputzip.gif)
Once downloaded, unzip the file and open 'index.html' in your browser. You should see a page like this:
@@ -104,8 +117,13 @@ Once downloaded, unzip the file and open 'index.html' in your browser. You shoul
Ensure that you can see the service(s) run on listed on the left pane.
You can navigate to the service(s) listed to see detailed findings on each service.
+
+
+Sample Output Walkthrough
+
![Sample Output](https://d39bs20xyg7k53.cloudfront.net/services-screener/p5-sample.gif)
+
## Using the report
The report provides you an easy-to-navigate dashboard of the various best-practice checks that were run.
diff --git a/info.json b/info.json
index ee6c4eb..02c428f 100644
--- a/info.json
+++ b/info.json
@@ -1 +1 @@
-{"cloudfront": 8, "cloudtrail": 17, "dynamodb": 24, "ec2": 49, "efs": 3, "eks": 7, "elasticache": 10, "guardduty": 4, "iam": 32, "kms": 2, "lambda": 14, "opensearch": 18, "rds": 65, "s3": 12}
\ No newline at end of file
+{"cloudfront": 8, "cloudtrail": 17, "dynamodb": 24, "ec2": 49, "efs": 3, "eks": 7, "elasticache": 10, "guardduty": 4, "iam": 32, "kms": 2, "lambda": 14, "opensearch": 18, "rds": 77, "s3": 12}
\ No newline at end of file
diff --git a/services/Service.py b/services/Service.py
index 435dd9b..3f866d4 100644
--- a/services/Service.py
+++ b/services/Service.py
@@ -38,21 +38,6 @@ def setRules(self, rules):
rules = rules.lower().split('^')
Config.set(self.RULESPREFIX, rules)
- def setTags(self, tags):
- rawTags = []
- if not tags:
- return
-
- result = []
- t = tags.split(self.TAGS_SEPARATOR)
- for tag in t:
- k, v = tag.split(self.KEYVALUE_SEPARATOR)
- rawTags = {k: v.split(self.VALUES_SEPARATOR) for k, v in tag.items()}
- result.append({"Name": "tag:" + k, "Values": v.split(self.VALUES_SEPARATOR)})
-
- self._tags = rawTags
- self.tags = result
-
def __del__(self):
timespent = round(time.time() - self.overallTimeStart, 3)
print('\033[1;42mCOMPLETED\033[0m -- ' + self.__class__.__name__.upper() + '::'+self.region+' (' + str(timespent) + 's)')
@@ -67,6 +52,23 @@ def __del__(self):
Config.set(self.RULESPREFIX, [])
+ def setTags(self, tags):
+ rawTags = {}
+ if not tags:
+ return
+
+ result = []
+ t = tags.split(self.TAGS_SEPARATOR)
+ for tag in t:
+ k, v = tag.split(self.KEYVALUE_SEPARATOR)
+ rawTags[k] = v.split(self.VALUES_SEPARATOR)
+ result.append({"Name": "tag:" + k, "Values": v.split(self.VALUES_SEPARATOR)})
+
+ self._tags = rawTags
+ self.tags = result
+
+ # print(self._tags, self.tags)
+
def resourceHasTags(self, tags):
if not self._tags:
return True
@@ -94,6 +96,22 @@ def resourceHasTags(self, tags):
return False
return True
+
+ # convert normal keypair to tag format
+ # {env: prod, costcenter: hr} => [{'Key': 'env', 'Value': 'prod'}, {'Key': 'costcenter', 'Value': 'hr'}]
+ def convertKeyPairTagToTagFormat(self, tags):
+ nTags = []
+ for k, v in tags.items():
+ nTags.append({'Key': k, 'Value': v})
+
+ return nTags
+
+ def convertTagKeyTagValueIntoKeyValue(self, tags):
+ nTags = []
+ for i in tags:
+ nTags.append({'Key': i['TagKey'], 'Value': i['TagValue']})
+
+ return nTags
if __name__ == "__main__":
Config.init()
diff --git a/services/cloudfront/Cloudfront.py b/services/cloudfront/Cloudfront.py
index b1aeb80..d97195a 100644
--- a/services/cloudfront/Cloudfront.py
+++ b/services/cloudfront/Cloudfront.py
@@ -24,13 +24,22 @@ def getDistributions(self):
while True:
if "DistributionList" in response and "Items" in response["DistributionList"]:
for dist in response["DistributionList"]["Items"]:
- arr.append(dist["Id"])
+ toAppend = True
+ if self.tags:
+ myTags = self.cloudfrontClient.list_tags_for_resource(Resource=dist['ARN'])
+ if self.resourceHasTags(myTags.get('Tags')['Items']) == False:
+ toAppend = False
+
+ if toAppend:
+ arr.append(dist["Id"])
+
if "NextMarker" not in response["DistributionList"]:
break
response = self.cloudfrontClient.list_distributions(Marker=response["DistributionList"]["NextMarker"])
else:
break
+
return arr
diff --git a/services/cloudtrail/Cloudtrail.py b/services/cloudtrail/Cloudtrail.py
index 52eba07..a48b874 100644
--- a/services/cloudtrail/Cloudtrail.py
+++ b/services/cloudtrail/Cloudtrail.py
@@ -4,6 +4,7 @@
import time
from utils.Config import Config
+from botocore.config import Config as bConfig
from services.Service import Service
from services.cloudtrail.drivers.CloudtrailCommon import CloudtrailCommon
from services.cloudtrail.drivers.CloudtrailAccount import CloudtrailAccount
@@ -30,7 +31,24 @@ def getTrails(self):
resp = ctClient.list_trails(NextToken = resp.get('NextToken'))
results += resp.get('Trails')
- return results
+
+ if not self.tags:
+ return results
+
+ finalArr = []
+ for i, detail in enumerate(results):
+ ctInfo = detail['TrailARN'].split(':')
+
+ ## despite cloudtrail seems like a "global api", for list_tags, need to call based on region tho.
+ ## need to create separate boto instance for that region
+ myTmpCtClient = self.ssBoto.client('cloudtrail', config=bConfig(region_name=ctInfo[3]))
+ tags = myTmpCtClient.list_tags(ResourceIdList=[detail['TrailARN']])
+
+ if self.resourceHasTags(tags.get('ResourceTagList')[0]['TagsList']):
+ finalArr.append(results[i])
+
+ return finalArr
+
def advise(self):
## Will loop through all trail, and set to True if any has MultiRegion
diff --git a/services/dynamodb/Dynamodb.py b/services/dynamodb/Dynamodb.py
index 35358da..095605b 100644
--- a/services/dynamodb/Dynamodb.py
+++ b/services/dynamodb/Dynamodb.py
@@ -40,7 +40,17 @@ def list_tables(self):
tableDescription = self.dynamoDbClient.describe_table(TableName = tables)
tableArr.append(tableDescription)
- return tableArr
+ if not self.tags:
+ return tableArr
+
+ finalArr = []
+ for i, detail in enumerate(tableArr):
+ tableArn = detail['Table']['TableArn']
+ tags = self.dynamoDbClient.list_tags_of_resource(ResourceArn=tableArn)
+ if self.resourceHasTags(tags.get('Tags')):
+ finalArr.append(tableArr[i])
+
+ return finalArr
except botocore.exceptions.ClientError as e:
ecode = e.response['Error']['Code']
diff --git a/services/ec2/Ec2.py b/services/ec2/Ec2.py
index f3c01eb..d5c383b 100644
--- a/services/ec2/Ec2.py
+++ b/services/ec2/Ec2.py
@@ -112,7 +112,15 @@ def getEC2SecurityGroups(self,instance):
)
arr = arr + results.get('SecurityGroups')
- return arr
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ if 'Tags' in detail and self.resourceHasTags(detail['Tags']):
+ finalArr.append(arr[i])
+
+ return finalArr
def getEBSResources(self):
filters = []
@@ -146,24 +154,22 @@ def getELB(self):
## TO DO: support tagging later
- # if self.tags is None:
- # return arr
-
- # filteredResults = []
- # for lb in arr:
- # tagResults = self.elbClient.describe_tags(
- # ResourceArns = [lb['LoadBalancerArn']]
- # )
- # tagDesc = tagResults.get('TagDescriptions')
- # if len(tagDesc) > 0:
- # for desc in tagDesc:
- # if self.resourceHasTags(desc['Tags']):
- # filteredResults.append(lb)
- # break
-
- # return filteredResults
+ if not self.tags:
+ return arr
- return arr
+ filteredResults = []
+ for lb in arr:
+ tagResults = self.elbClient.describe_tags(
+ ResourceArns = [lb['LoadBalancerArn']]
+ )
+ tagDesc = tagResults.get('TagDescriptions')
+ if len(tagDesc) > 0:
+ for desc in tagDesc:
+ if self.resourceHasTags(desc['Tags']):
+ filteredResults.append(lb)
+ break
+
+ return filteredResults
def getELBClassic(self):
results = self.elbClassicClient.describe_load_balancers()
@@ -208,6 +214,14 @@ def getELBSecurityGroup(self, elb):
NextToken = results.get('NextToken')
)
arr = arr + results.get('SecurityGroups')
+
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ if self.resourceHasTags(detail['Tags']):
+ finalArr.append(arr[i])
return arr
@@ -240,7 +254,15 @@ def getEIPResources(self):
)
arr = result.get('Addresses')
- return arr
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ if 'Tags' in detail and self.resourceHasTags(detail['Tags']):
+ finalArr.append(arr[i])
+
+ return finalArr
def getDefaultSG(self):
defaultSGs = {}
@@ -257,7 +279,16 @@ def getDefaultSG(self):
if group.get('GroupName') == 'default':
defaultSGs[group.get('GroupId')] = group
- return defaultSGs
+ if not self.tags:
+ return defaultSGs
+
+ finalArr = []
+
+ for i, detail in defaultSGs.items():
+ if 'Tags' in detail and self.resourceHasTags(detail['Tags']):
+ finalArr.append(defaultSGs[i])
+
+ return finalArr
def advise(self):
objs = {}
@@ -362,18 +393,20 @@ def advise(self):
objs[f"ASG::{group['AutoScalingGroupName']}"] = obj.getInfo()
defaultSGs = self.getDefaultSG()
- for groupId in defaultSGs.keys():
- if groupId not in secGroups:
- secGroups[groupId] = defaultSGs[groupId]
- secGroups[groupId]['inUsed'] = 'False'
+ if defaultSGs:
+ for groupId in defaultSGs.keys():
+ if groupId not in secGroups:
+ secGroups[groupId] = defaultSGs[groupId]
+ secGroups[groupId]['inUsed'] = 'False'
# SG checks
- for group in secGroups.values():
- print(f"... (EC2::Security Group) inspecting {group['GroupId']}")
- obj = Ec2SecGroup(group, self.ec2Client)
- obj.run(self.__class__)
-
- objs[f"SG::{group['GroupId']}"] = obj.getInfo()
+ if secGroups:
+ for group in secGroups.values():
+ print(f"... (EC2::Security Group) inspecting {group['GroupId']}")
+ obj = Ec2SecGroup(group, self.ec2Client)
+ obj.run(self.__class__)
+
+ objs[f"SG::{group['GroupId']}"] = obj.getInfo()
# EIP checks
eips = self.getEIPResources()
diff --git a/services/efs/Efs.py b/services/efs/Efs.py
index 02f4063..185c95c 100644
--- a/services/efs/Efs.py
+++ b/services/efs/Efs.py
@@ -23,7 +23,7 @@ def get_resources(self):
filtered_results = []
for efs in results:
- if self.resource_has_tags(efs['Tags']):
+ if self.resourceHasTags(efs['Tags']):
filtered_results.append(efs)
return filtered_results
diff --git a/services/eks/Eks.py b/services/eks/Eks.py
index c943c4f..e3c8fa0 100644
--- a/services/eks/Eks.py
+++ b/services/eks/Eks.py
@@ -44,9 +44,16 @@ def advise(self):
for cluster in clusters:
print('...(EKS:Cluster) inspecting ' + cluster)
clusterInfo = self.describeCluster(cluster)
- if clusterInfo.get('status') == 'CREATING':
- print(cluster + " cluster is creating. Skipped")
- continue
+
+ #if clusterInfo.get('status') == 'CREATING':
+ # print(cluster + " cluster is creating. Skipped")
+ # continue
+
+ if self.tags:
+ resp = self.eksClient.list_tags_for_resource(resourceArn=clusterInfo['arn'])
+ nTags = self.convertKeyPairTagToTagFormat(resp.get('tags'))
+ if self.resourceHasTags(nTags) == False:
+ continue
obj = EksCommon(cluster, clusterInfo, self.eksClient, self.ec2Client, self.iamClient)
obj.run(self.__class__)
diff --git a/services/elasticache/Elasticache.py b/services/elasticache/Elasticache.py
index 08a6040..08183f7 100644
--- a/services/elasticache/Elasticache.py
+++ b/services/elasticache/Elasticache.py
@@ -38,8 +38,23 @@ def getECClusterInfo(self):
except botocore.exceptions.ClientError as e:
# print out error to console for now
print(e)
+
+ fArr = []
+ for i, detail in enumerate(arr):
+ if detail['CacheClusterStatus'] == 'available':
+ fArr.append(arr[i])
- return arr
+ if not self.tags:
+ return fArr
+
+ finalArr = []
+ for i, detail in enumerate(fArr):
+ tag = self.elasticacheClient.list_tags_for_resource(ResourceName=detail['ARN'])
+ nTag = tag.get('TagList')
+ if self.resourceHasTags(nTag):
+ finalArr.append(arr[i])
+
+ return finalArr
def getEngineVersions(self) -> Dict[str, List]:
lookup = {}
@@ -100,10 +115,25 @@ def getReplicationGroupInfo(self):
Marker=results.get("Marker")
)
arr = arr + results.get("ReplicationGroups")
-
- return arr
-
+
+ fArr = []
+ for i, detail in enumerate(arr):
+ if detail['Status'] == 'available':
+ fArr.append(arr[i])
+
+ if not self.tags:
+ return fArr
+
+ finalArr = []
+ for i, detail in enumerate(fArr):
+ tag = self.elasticacheClient.list_tags_for_resource(ResourceName=detail['ARN'])
+ nTag = tag.get('TagList')
+ if self.resourceHasTags(nTag):
+ finalArr.append(arr[i])
+
+ return finalArr
+ ## NOT IN USED
def getSnapshots(self):
replicationGroupId = set()
last_updated = {}
@@ -150,7 +180,7 @@ def advise(self):
# loop through EC nodes
if len(self.cluster_info) > 0:
- print("evaluating Elasticache Clusters")
+ # print("evaluating Elasticache Clusters")
self.driverInfo = {}
self.driverInfo['engine_veresions'] = self.getEngineVersions()
self.driverInfo['latest_instances'] = self.getLatestInstanceTypes()
diff --git a/services/iam/Iam.py b/services/iam/Iam.py
index 785d070..5265b08 100644
--- a/services/iam/Iam.py
+++ b/services/iam/Iam.py
@@ -29,6 +29,8 @@ def __init__(self, region):
'ctClient': ssBoto.client('cloudtrail', config=self.bConfig)
}
+ ## Groups has no TAG attribute
+ ## Unable to implement "TAG" filter
def getGroups(self):
arr = []
results = self.iamClient.list_groups()
@@ -55,7 +57,17 @@ def getRoles(self):
if (v['Path'] != '/service-role/' and v['Path'][0:18] != '/aws-service-role/') and (self._roleFilterByName(v['RoleName'])):
arr.append(v)
- return arr
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ tag = self.iamClient.list_role_tags(RoleName=detail['RoleName'])
+ nTag = tag.get('Tags')
+ if self.resourceHasTags(nTag):
+ finalArr.append(arr[i])
+
+ return finalArr
def getUsers(self):
self.getUserFlag = True
@@ -101,7 +113,22 @@ def getUsers(self):
for temp in row:
arr.append(dict(zip(fields, temp.split(','))))
- return arr
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ if detail['user'] == '':
+ finalArr.append(arr[i])
+ continue
+
+ tag = self.iamClient.list_user_tags(UserName=detail['user'])
+ nTag = tag.get('Tags')
+ if self.resourceHasTags(nTag):
+ finalArr.append(arr[i])
+
+
+ return finalArr
def advise(self):
objs = {}
diff --git a/services/kms/Kms.py b/services/kms/Kms.py
index ccacea2..d69a75f 100644
--- a/services/kms/Kms.py
+++ b/services/kms/Kms.py
@@ -32,6 +32,12 @@ def checkKmsKey(self, resp):
rr = self.kmsClient.get_key_rotation_status(KeyId = key['KeyId'])
metadata['KeyRotationEnabled'] = rr.get('KeyRotationEnabled')
+ if self.tags:
+ tags = self.kmsClient.list_resource_tags(KeyId = key['KeyId'])
+ nTags = self.convertTagKeyTagValueIntoKeyValue(tags.get('Tags'))
+ if self.resourceHasTags(nTags) == False:
+ continue
+
self.kmsCustomerManagedKeys.append(metadata)
return []
diff --git a/services/lambda_/Lambda.py b/services/lambda_/Lambda.py
index ab15367..83844e0 100644
--- a/services/lambda_/Lambda.py
+++ b/services/lambda_/Lambda.py
@@ -40,26 +40,16 @@ def get_resources(self):
filtered_functions = []
for function in functions:
- try:
- response = self.lambda_client.list_tags(
- Resource=function["FunctionArn"]
- )
- tags = response["Tags"]
- if self.resource_has_tags(tags):
- filtered_functions.append(function)
- except ClientError as e:
- print(f"Error listing tags for {function['FunctionArn']}: {e}")
+ response = self.lambda_client.list_tags(
+ Resource=function["FunctionArn"]
+ )
+ tags = response.get("Tags")
+ nTags = self.convertKeyPairTagToTagFormat(tags)
+ if self.resourceHasTags(nTags):
+ filtered_functions.append(function)
return filtered_functions
- def resource_has_tags(self, tags):
- for tag in self.tags:
- key = tag["Name"].replace("tag:", "")
- value = tag["Values"][0]
- if key in tags and tags[key] == value:
- return True
- return False
-
def advise(self):
objs = {}
func_role_map = {}
diff --git a/services/opensearch/Opensearch.py b/services/opensearch/Opensearch.py
index 4e6774d..f264de7 100644
--- a/services/opensearch/Opensearch.py
+++ b/services/opensearch/Opensearch.py
@@ -18,8 +18,27 @@ def __init__(self, region):
def getResources(self):
arr = []
+ fArr = []
results = self.osClient.list_domain_names()
- return results.get('DomainNames')
+
+ arr = results.get('DomainNames')
+ for domain in arr:
+ r = self.osClient.describe_domain(DomainName=domain['DomainName'])
+ info = r.get('DomainStatus')
+ if info['Processing'] == True or info['Deleted'] == True:
+ continue
+
+ t = domain
+ t['info'] = info
+ if not self.tags:
+ fArr.append(t)
+ else:
+ tags = self.osClient.list_tags(ARN=info['ARN'])
+ nTags = tags.get('TagList')
+ if self.resourceHasTags(nTags):
+ fArr.append(t)
+
+ return fArr
def advise(self):
domains = self.getResources()
@@ -29,7 +48,7 @@ def advise(self):
domain_name = domain["DomainName"]
print("... (OpenSearch) inspecting " + domain_name)
- obj = OpensearchCommon(self.bConfig, domain_name, self.osClient, self.cwClient)
+ obj = OpensearchCommon(self.bConfig, domain_name, domain['info'], self.osClient, self.cwClient)
obj.run(self.__class__)
#objs["OpenSearch::Common"] = obj.getInfo()
diff --git a/services/opensearch/drivers/OpensearchCommon.py b/services/opensearch/drivers/OpensearchCommon.py
index 96ace60..22eb0fb 100644
--- a/services/opensearch/drivers/OpensearchCommon.py
+++ b/services/opensearch/drivers/OpensearchCommon.py
@@ -11,14 +11,15 @@
class OpensearchCommon(Evaluator):
NODES_LIMIT = 200
- def __init__(self, bConfig, domain, osClient, cwClient):
+ def __init__(self, bConfig, domain, attr, osClient, cwClient):
self.results = {}
self.clientConfig = bConfig
self.domain = domain
self.osClient = osClient
self.cwClient = cwClient
- self.attribute = self.osClient.describe_domain(DomainName=self.domain)
+ # self.attribute = self.osClient.describe_domain(DomainName=self.domain)
+ self.attribute = {'DomainStatus': attr}
self.cluster_config = self.attribute["DomainStatus"]["ClusterConfig"]
self.domain_config = self.osClient.describe_domain_config(DomainName=self.domain)
@@ -176,6 +177,9 @@ def _checkEbsStorageUtilisation(self):
stats = self.getCloudWatchData(metric)
dp = stats.get("Datapoints")
+ if len(dp) == 0:
+ return
+
free_space = dp[0]["Average"]
try:
@@ -212,7 +216,11 @@ def _checkReplicaShard(self):
primary = "Shards.activePrimary"
stats_active = self.getCloudWatchData(active)
- dp_active = stats_active.get("Datapoints")[0]["Average"]
+ dp = stats_active.get("Datapoints")
+ if len(dp) == 0:
+ return
+
+ dp_active = dp[0]["Average"]
stats_primary = self.getCloudWatchData(primary)
dp_primary = stats_primary.get("Datapoints")[0]["Average"]
diff --git a/services/rds/Rds.py b/services/rds/Rds.py
index 59f7ab3..49a0eb0 100644
--- a/services/rds/Rds.py
+++ b/services/rds/Rds.py
@@ -36,13 +36,14 @@ def __init__(self, region):
}
def getResources(self):
- results = self.rdsClient.describe_db_instances()
+ p = {}
+
+ results = self.rdsClient.describe_db_instances(**p)
arr = results.get('DBInstances')
while results.get('Maker') is not None:
- results = self.rdsClient.describe_db_instances(
- Maker = results.get('Maker')
- )
+ p['Maker'] = results.get('Maker')
+ results = self.rdsClient.describe_db_instances(**p)
arr = arr + results.get('DBInstances')
for k, v in enumerate(arr):
@@ -69,21 +70,37 @@ def getClusters(self):
results = self.rdsClient.describe_db_clusters(**p)
arr = arr + results.get('DBClusters')
+
+ if not self.tags:
+ return arr
+
+ finalArr = []
+ for i, detail in enumerate(arr):
+ if self.resourceHasTags(detail['TagList']):
+ finalArr.append(arr[i])
- return arr
+ return finalArr
def getSecrets(self):
- results = self.smClient.list_secrets(IncludePlannedDeletion=False, MaxResults=10)
+ p = {"IncludePlannedDeletion": False, "MaxResults": 10}
+
+ results = self.smClient.list_secrets(**p)
self.registerSecrets(results)
+
NextToken = results.get('NextToken')
while NextToken != None:
- results = self.smClient.list_secrets(IncludePlannedDeletion=False, MaxResults=10, NextToken=NextToken)
+ p['NextToken'] = NextToken
+ results = self.smClient.list_secrets(**p)
NextToken = results.get('NextToken')
self.registerSecrets(results)
def registerSecrets(self, results):
for secret in results.get('SecretList'):
+ if self.tags:
+ if self.resourceHasTags(secret['Tags']) == False:
+ continue
+
resp = self.smClient.describe_secret(SecretId=secret['ARN'])
self.secrets.append(resp)
diff --git a/services/rds/drivers/RdsCommon.py b/services/rds/drivers/RdsCommon.py
index 02ea046..7717049 100644
--- a/services/rds/drivers/RdsCommon.py
+++ b/services/rds/drivers/RdsCommon.py
@@ -390,23 +390,23 @@ def _checkOldSnapshots(self):
snapshots = snapshots + result.get('DBSnapshots')
- if not snapshots:
- return
+ if not snapshots:
+ return
- oldest_copy = snapshots[-1]
-
- oldest_copy_date = oldest_copy['SnapshotCreateTime']
-
- now = datetime.datetime.now(timezone.utc)
-
- diff = now - oldest_copy_date
- days = diff.days
-
- if len(snapshots) > 5:
- self.results['SnapshotTooMany'] = [-1, len(snapshots)]
+ oldest_copy = snapshots[-1]
+
+ oldest_copy_date = oldest_copy['SnapshotCreateTime']
+
+ now = datetime.datetime.now(timezone.utc)
- if days > 180:
- self.results['SnapshotTooOld'] = [-1, days]
+ diff = now - oldest_copy_date
+ days = diff.days
+
+ if len(snapshots) > 5:
+ self.results['ManualSnapshotTooMany'] = [-1, len(snapshots)]
+
+ if days > 180:
+ self.results['ManualSnapshotTooOld'] = [-1, days]
def _checkCAExpiry(self):
if self.isCluster == False and self.certInfo['isExpireIn365days'] == True:
diff --git a/services/rds/rds.reporter.json b/services/rds/rds.reporter.json
index d35dd09..d09d5a1 100644
--- a/services/rds/rds.reporter.json
+++ b/services/rds/rds.reporter.json
@@ -981,5 +981,33 @@
"[Mysql Doc on innodb_open_files]",
"[AWS view on innodb_open_files]"
]
+ },
+ "ManualSnapshotTooOld":{
+ "category": "C",
+ "^description": "[Cost Optimisation] You have {$COUNT} RDS/Aurora MANUAL Snapshot instance(s) is having age of more than 180 days. Review if this snapshot is still required. Considering export and backup data into S3 Glacier for long term archival for cost saving purposes.",
+ "downtime": 0,
+ "slowness": 0,
+ "additionalCost": 0,
+ "needFullTest": 1,
+ "criticality": "L",
+ "shortDesc": "Check if old RDS snapshot(s) is required/relevant",
+ "ref": [
+ "[RDS - Delete Snapshot]",
+ "[RDS Snapshot retention cost]"
+ ]
+ },
+ "ManualSnapshotTooMany":{
+ "category": "C",
+ "^description": "[Cost Optimisation] You have {$COUNT} RDS/Aurora MANUAL Snapshot instance(s) is having more than 5 manual snapshot. Review if this snapshot is still required. Considering export and backup data into S3 Glacier for long term archival for cost saving purposes.",
+ "downtime": 0,
+ "slowness": 0,
+ "additionalCost": 0,
+ "needFullTest": 1,
+ "criticality": "L",
+ "shortDesc": "Check if lists of RDS snapshots are required/relevant",
+ "ref": [
+ "[RDS - Delete Snapshot]",
+ "[RDS Snapshot retention cost]"
+ ]
}
}
\ No newline at end of file
diff --git a/services/s3/S3.py b/services/s3/S3.py
index 1f601f4..169e2ad 100644
--- a/services/s3/S3.py
+++ b/services/s3/S3.py
@@ -5,7 +5,7 @@
from utils.Config import Config
-from utils.Tools import _pr
+from utils.Tools import _pr, _warn
from services.Service import Service
from botocore.config import Config as bConfig
@@ -73,19 +73,17 @@ def getResources(self):
return _buckets
filteredBuckets = []
- '''
- # to support tagging
for bucket in _buckets:
try:
result = self.s3Client.get_bucket_tagging(Bucket = bucket['Name'])
tags =result.get('TagSet')
-
if self.resourceHasTags(tags):
filteredBuckets.append(bucket)
- except S3E as e:
- ## Do nothing, no tags has been define;clear
- pass
- '''
+ except botocore.exceptions.ClientError as e:
+ if e.response['Error']['Code'] != 'NoSuchTagSet':
+ emsg = e.response['Error']
+ _warn("S3 Error:({}, {}) is not being handled by S3::Service, please submit an issue to github.".format(emsg['Code'], emsg['Message']))
+
return filteredBuckets
def advise(self):