Here is the script to find a un tagged resources in AWS:
import boto3
import datetime
from botocore.exceptions import ClientError
import json
class AWSTagRecommender:
def __init__(self, region='us-east-1'):
self.region = region
self.ec2 = boto3.client('ec2', region_name=region)
self.rds = boto3.client('rds', region_name=region)
self.s3 = boto3.client('s3')
self.lambda_client = boto3.client('lambda', region_name=region)
def recommended_tags(self, resource_type, resource_details=None):
"""Generate recommended tags based on resource type and details"""
current_date = datetime.datetime.now().strftime('%Y-%m-%d')
# Base recommended tags
base_tags = {
'Environment': ['prod', 'dev', 'stage', 'test'],
'Owner': 'REQUIRED',
'CostCenter': 'REQUIRED',
'Project': 'REQUIRED',
'CreatedDate': current_date,
'Backup': ['true', 'false'],
'SecurityLevel': ['high', 'medium', 'low']
}
# Resource-specific tag recommendations
specific_tags = {
'ec2': {
'ApplicationRole': ['web', 'app', 'db', 'cache'],
'PatchGroup': ['group1', 'group2', 'critical'],
'AutoStop': ['true', 'false']
},
'rds': {
'DatabaseType': ['mysql', 'postgres', 'oracle', 'sqlserver'],
'BackupRetention': ['7days', '30days', '90days'],
'DataClassification': ['public', 'private', 'confidential']
},
's3': {
'DataType': ['logs', 'backups', 'user-content', 'static-assets'],
'AccessPattern': ['frequent', 'infrequent', 'archive'],
'LifecyclePolicy': ['required', 'not-required']
},
'lambda': {
'FunctionType': ['api', 'scheduled', 'event-driven'],
'Runtime': resource_details.get('Runtime', 'unknown') if resource_details else 'REQUIRED',
'APIEndpoint': ['true', 'false']
}
}
return {**base_tags, **specific_tags.get(resource_type, {})}
def find_untagged_ec2(self):
"""Find untagged EC2 resources"""
try:
instances = self.ec2.describe_instances()
untagged = []
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
if not instance.get('Tags'):
untagged.append({
'ResourceId': instance['InstanceId'],
'Type': 'ec2',
'Details': {
'InstanceType': instance['InstanceType'],
'State': instance['State']['Name'],
'LaunchTime': instance['LaunchTime'].strftime('%Y-%m-%d')
}
})
return untagged
except ClientError as e:
print(f"Error finding untagged EC2 instances: {e}")
return []
def find_untagged_rds(self):
"""Find untagged RDS resources"""
try:
instances = self.rds.describe_db_instances()
untagged = []
for instance in instances['DBInstances']:
tags = self.rds.list_tags_for_resource(
ResourceName=instance['DBInstanceArn']
)['TagList']
if not tags:
untagged.append({
'ResourceId': instance['DBInstanceIdentifier'],
'Type': 'rds',
'Details': {
'Engine': instance['Engine'],
'Class': instance['DBInstanceClass'],
'Storage': instance['AllocatedStorage']
}
})
return untagged
except ClientError as e:
print(f"Error finding untagged RDS instances: {e}")
return []
def find_untagged_s3(self):
"""Find untagged S3 buckets"""
try:
buckets = self.s3.list_buckets()['Buckets']
untagged = []
for bucket in buckets:
try:
tags = self.s3.get_bucket_tagging(Bucket=bucket['Name'])
except ClientError as e:
if e.response['Error']['Code'] == 'NoSuchTagSet':
untagged.append({
'ResourceId': bucket['Name'],
'Type': 's3',
'Details': {
'CreationDate': bucket['CreationDate'].strftime('%Y-%m-%d')
}
})
return untagged
except ClientError as e:
print(f"Error finding untagged S3 buckets: {e}")
return []
def find_untagged_lambda(self):
"""Find untagged Lambda functions"""
try:
functions = self.lambda_client.list_functions()['Functions']
untagged = []
for function in functions:
tags = self.lambda_client.list_tags(
Resource=function['FunctionArn']
).get('Tags', {})
if not tags:
untagged.append({
'ResourceId': function['FunctionName'],
'Type': 'lambda',
'Details': {
'Runtime': function['Runtime'],
'LastModified': function['LastModified']
}
})
return untagged
except ClientError as e:
print(f"Error finding untagged Lambda functions: {e}")
return []
def generate_report(self):
"""Generate a comprehensive report of untagged resources and recommendations"""
all_untagged = []
all_untagged.extend(self.find_untagged_ec2())
all_untagged.extend(self.find_untagged_rds())
all_untagged.extend(self.find_untagged_s3())
all_untagged.extend(self.find_untagged_lambda())
report = {
'generated_date': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'region': self.region,
'untagged_resources': []
}
for resource in all_untagged:
report['untagged_resources'].append({
'resource_id': resource['ResourceId'],
'resource_type': resource['Type'],
'details': resource['Details'],
'recommended_tags': self.recommended_tags(resource['Type'], resource['Details'])
})
return report
def main():
# Initialize the tag recommender
recommender = AWSTagRecommender()
# Generate the report
print("Analyzing AWS resources for missing tags...")
report = recommender.generate_report()
# Save the report to a file
filename = f"tag_recommendations_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(filename, 'w') as f:
json.dump(report, f, indent=2, default=str)
# Print summary
print(f"\nReport generated: {filename}")
print(f"Found {len(report['untagged_resources'])} untagged resources")
# Print resource type breakdown
resource_types = {}
for resource in report['untagged_resources']:
resource_types[resource['resource_type']] = resource_types.get(resource['resource_type'], 0) + 1
print("\nBreakdown by resource type:")
for rtype, count in resource_types.items():
print(f"{rtype}: {count} untagged resources")
if __name__ == "__main__":
main()
Hope you enjoyed the post.
Cheers
Ramasankar Molleti
