cloud-governance 1.1.369__py3-none-any.whl → 1.1.371__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.
- cloud_governance/common/clouds/aws/iam/iam_operations.py +157 -0
- cloud_governance/common/elasticsearch/modals/policy_es_data.py +2 -0
- cloud_governance/common/utils/configs.py +2 -0
- cloud_governance/main/environment_variables.py +3 -1
- cloud_governance/main/main_oerations/main_operations.py +1 -1
- cloud_governance/policy/aws/monthly_report.py +2 -1
- cloud_governance/policy/aws/unused_access_key.py +55 -0
- cloud_governance/policy/common_policies/send_aggregated_alerts.py +4 -2
- cloud_governance/policy/helpers/abstract_policy_operations.py +3 -3
- cloud_governance/policy/helpers/aws/aws_policy_operations.py +29 -2
- cloud_governance/policy/policy_operations/aws/zombie_non_cluster/zombie_non_cluster_polices.py +1 -1
- {cloud_governance-1.1.369.dist-info → cloud_governance-1.1.371.dist-info}/METADATA +2 -1
- {cloud_governance-1.1.369.dist-info → cloud_governance-1.1.371.dist-info}/RECORD +16 -15
- {cloud_governance-1.1.369.dist-info → cloud_governance-1.1.371.dist-info}/WHEEL +1 -1
- {cloud_governance-1.1.369.dist-info → cloud_governance-1.1.371.dist-info}/licenses/LICENSE +0 -0
- {cloud_governance-1.1.369.dist-info → cloud_governance-1.1.371.dist-info}/top_level.txt +0 -0
|
@@ -5,10 +5,13 @@ import boto3
|
|
|
5
5
|
from cloud_governance.common.clouds.aws.utils.common_methods import get_boto3_client
|
|
6
6
|
from cloud_governance.common.clouds.aws.utils.utils import Utils
|
|
7
7
|
from cloud_governance.common.logger.init_logger import logger
|
|
8
|
+
from datetime import datetime, timezone
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class IAMOperations:
|
|
11
12
|
|
|
13
|
+
ACCESS_KEY_LABEL_MAP = {"access key 1": 0, "access key 2": 1}
|
|
14
|
+
|
|
12
15
|
def __init__(self, iam_client=None):
|
|
13
16
|
self.iam_client = iam_client if iam_client else get_boto3_client('iam')
|
|
14
17
|
self.utils = Utils()
|
|
@@ -158,3 +161,157 @@ class IAMOperations:
|
|
|
158
161
|
return True
|
|
159
162
|
except Exception as err:
|
|
160
163
|
raise err
|
|
164
|
+
|
|
165
|
+
def tag_user(self, user_name: str, tags: list):
|
|
166
|
+
"""
|
|
167
|
+
This method tags the IAM user.
|
|
168
|
+
:param user_name: The name of the IAM user to tag.
|
|
169
|
+
:param tags: A list of tags to associate with the user.
|
|
170
|
+
:return: True if tagging is successful, otherwise raises an exception.
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
self.iam_client.tag_user(UserName=user_name, Tags=tags)
|
|
174
|
+
return True
|
|
175
|
+
except Exception as err:
|
|
176
|
+
raise err
|
|
177
|
+
|
|
178
|
+
def get_iam_users_access_keys(self):
|
|
179
|
+
"""
|
|
180
|
+
Retrieves IAM users and summarizes:
|
|
181
|
+
- Access key status (active/inactive)
|
|
182
|
+
- Access key age in days
|
|
183
|
+
- Access key last used in days (or "N/A" if never used)
|
|
184
|
+
- Tags (as a list of dictionaries)
|
|
185
|
+
- Most recent key usage: last_activity_days
|
|
186
|
+
- IAM client region (global context, since IAM is non-regional)
|
|
187
|
+
- IAM user unique ID: ResourceId
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
dict: {
|
|
191
|
+
"username": {
|
|
192
|
+
"Access key 1": [status, age_days, last_used_days],
|
|
193
|
+
"Access key 2": [...],
|
|
194
|
+
"last_activity_days": int or "N/A",
|
|
195
|
+
"tags": [{"Key": "tag_key", "Value": "tag_value"}, ...],
|
|
196
|
+
"region": "us-east-1",
|
|
197
|
+
"ResourceId": "AIDAEXAMPLEUSERID"
|
|
198
|
+
},
|
|
199
|
+
...
|
|
200
|
+
}
|
|
201
|
+
"""
|
|
202
|
+
result = {}
|
|
203
|
+
now = datetime.now(timezone.utc)
|
|
204
|
+
region_name = self.iam_client.meta.region_name or "global"
|
|
205
|
+
|
|
206
|
+
paginator = self.iam_client.get_paginator('list_users')
|
|
207
|
+
for page in paginator.paginate():
|
|
208
|
+
for user in page['Users']:
|
|
209
|
+
username = user['UserName']
|
|
210
|
+
result[username] = {}
|
|
211
|
+
# Access keys
|
|
212
|
+
access_keys = self.iam_client.list_access_keys(UserName=username)['AccessKeyMetadata']
|
|
213
|
+
for idx, key in enumerate(access_keys, start=1):
|
|
214
|
+
label = f"Access key {idx}"
|
|
215
|
+
status = key['Status'].lower()
|
|
216
|
+
age_days = (now - key['CreateDate']).days
|
|
217
|
+
|
|
218
|
+
# Get access key last used
|
|
219
|
+
try:
|
|
220
|
+
response = self.iam_client.get_access_key_last_used(AccessKeyId=key['AccessKeyId'])
|
|
221
|
+
last_used_date = response.get('AccessKeyLastUsed', {}).get('LastUsedDate')
|
|
222
|
+
if last_used_date:
|
|
223
|
+
last_used_days = (now - last_used_date).days
|
|
224
|
+
else:
|
|
225
|
+
last_used_days = "N/A"
|
|
226
|
+
except Exception:
|
|
227
|
+
last_used_days = "N/A"
|
|
228
|
+
|
|
229
|
+
result[username][label] = {'label': label, 'status': status, 'age_days': age_days, 'last_activity_days': last_used_days if last_used_days is not None else "N/A"}
|
|
230
|
+
|
|
231
|
+
# Tags as list of dicts
|
|
232
|
+
try:
|
|
233
|
+
tag_response = self.iam_client.list_user_tags(UserName=username)
|
|
234
|
+
tags = tag_response.get('Tags', [])
|
|
235
|
+
except Exception:
|
|
236
|
+
tags = []
|
|
237
|
+
|
|
238
|
+
result[username]["tags"] = tags
|
|
239
|
+
result[username]["region"] = region_name
|
|
240
|
+
result[username]["ResourceId"] = user.get('UserId') # <-- Unique ID
|
|
241
|
+
|
|
242
|
+
return result
|
|
243
|
+
|
|
244
|
+
def has_active_access_keys(self, username: str, access_key_label: str = None) -> bool:
|
|
245
|
+
"""
|
|
246
|
+
Checks if the given IAM user has any active access keys.
|
|
247
|
+
Optionally filters by access key label ("Access Key 1" or "Access Key 2").
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
username (str): IAM user name
|
|
251
|
+
access_key_label (str): Label to filter access keys ("Access Key 1"/"Access Key 2")
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
bool: True if any access key is active (and matches the label if provided), False otherwise
|
|
255
|
+
"""
|
|
256
|
+
try:
|
|
257
|
+
keys = self.iam_client.list_access_keys(UserName=username)['AccessKeyMetadata']
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"Failed to list access keys for user '{username}': {e}")
|
|
260
|
+
return False
|
|
261
|
+
|
|
262
|
+
# Sort keys by CreateDate ascending (oldest first)
|
|
263
|
+
keys.sort(key=lambda k: k['CreateDate'])
|
|
264
|
+
|
|
265
|
+
if access_key_label:
|
|
266
|
+
idx = self.ACCESS_KEY_LABEL_MAP.get(access_key_label.lower())
|
|
267
|
+
if idx is None or idx >= len(keys):
|
|
268
|
+
return False
|
|
269
|
+
return keys[idx].get('Status') == 'Active'
|
|
270
|
+
|
|
271
|
+
return any(k.get('Status') == 'Active' for k in keys)
|
|
272
|
+
|
|
273
|
+
def deactivate_user_access_key(self, username: str, **kwargs):
|
|
274
|
+
"""
|
|
275
|
+
Deactivates the specified access key for the given IAM user.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
username (str): IAM user name
|
|
279
|
+
access_key_label (str): Access Key 1 or Access Key 2 (case-insensitive)
|
|
280
|
+
"""
|
|
281
|
+
access_key_label = kwargs.get('access_key_label', '').lower()
|
|
282
|
+
if not access_key_label:
|
|
283
|
+
logger.warning("No access key label provided for deactivation.")
|
|
284
|
+
return
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
access_keys = self.iam_client.list_access_keys(UserName=username)['AccessKeyMetadata']
|
|
288
|
+
except Exception as e:
|
|
289
|
+
logger.error(f"Failed to list access keys for user '{username}': {e}")
|
|
290
|
+
return
|
|
291
|
+
|
|
292
|
+
# Sort keys by CreateDate ascending (oldest first) for consistent indexing
|
|
293
|
+
access_keys.sort(key=lambda k: k['CreateDate'])
|
|
294
|
+
|
|
295
|
+
idx = self.ACCESS_KEY_LABEL_MAP.get(access_key_label)
|
|
296
|
+
if idx is None or idx >= len(access_keys):
|
|
297
|
+
logger.warning(f"Access key label '{access_key_label}' not found for user '{username}'")
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
key_to_deactivate = access_keys[idx]
|
|
301
|
+
access_key_id = key_to_deactivate['AccessKeyId']
|
|
302
|
+
current_status = key_to_deactivate['Status'].lower()
|
|
303
|
+
|
|
304
|
+
if current_status == 'active':
|
|
305
|
+
try:
|
|
306
|
+
self.iam_client.update_access_key(
|
|
307
|
+
UserName=username,
|
|
308
|
+
AccessKeyId=access_key_id,
|
|
309
|
+
Status='Inactive'
|
|
310
|
+
)
|
|
311
|
+
logger.info(f"Access key '{access_key_id}' deactivated for user '{username}'")
|
|
312
|
+
except Exception as e:
|
|
313
|
+
logger.error(f"Failed to deactivate access key '{access_key_id}' for user '{username}': {e}")
|
|
314
|
+
else:
|
|
315
|
+
logger.info(f"Access key '{access_key_id}' is already inactive for user '{username}'")
|
|
316
|
+
|
|
317
|
+
logger.info(f"Access key deactivation processed for user '{username}'.")
|
|
@@ -22,6 +22,8 @@ INSTANCE_IDLE_NETWORK_OUT_KILO_BYTES = 5 # In KiB
|
|
|
22
22
|
EC2_NAMESPACE = 'AWS/EC2'
|
|
23
23
|
CLOUDWATCH_METRICS_AVAILABLE_DAYS = 14
|
|
24
24
|
AWS_DEFAULT_GLOBAL_REGION = 'us-east-1'
|
|
25
|
+
UNUSED_ACCESS_KEY_DAYS = 90
|
|
26
|
+
UNUSED_ACCESS_KEY_MAX_DAY = 1000
|
|
25
27
|
|
|
26
28
|
# X86 to Graviton
|
|
27
29
|
GRAVITON_MAPPINGS = {
|
|
@@ -98,7 +98,7 @@ class EnvironmentVariables:
|
|
|
98
98
|
'ip_unattached', 'unused_nat_gateway',
|
|
99
99
|
'instance_idle',
|
|
100
100
|
'ec2_stop', 'ebs_in_use', 'database_idle',
|
|
101
|
-
's3_inactive',
|
|
101
|
+
's3_inactive', 'unused_access_key',
|
|
102
102
|
'empty_roles',
|
|
103
103
|
'zombie_snapshots', 'skipped_resources',
|
|
104
104
|
'monthly_report', 'optimize_resources_report']
|
|
@@ -283,6 +283,8 @@ class EnvironmentVariables:
|
|
|
283
283
|
self._environment_variables_dict['POLICIES_TO_ALERT'] = literal_eval(
|
|
284
284
|
EnvironmentVariables.get_env('POLICIES_TO_ALERT', '[]'))
|
|
285
285
|
self._environment_variables_dict['ADMIN_MAIL_LIST'] = EnvironmentVariables.get_env('ADMIN_MAIL_LIST', '')
|
|
286
|
+
self._environment_variables_dict['SKIP_POLICIES_ALERT'] = literal_eval(
|
|
287
|
+
EnvironmentVariables.get_env('SKIP_POLICIES_ALERT', "['']"))
|
|
286
288
|
if self._environment_variables_dict.get('policy') in ['send_aggregated_alerts', 'cloudability_cost_reports']:
|
|
287
289
|
self._environment_variables_dict['COMMON_POLICIES'] = True
|
|
288
290
|
# CRO -- Cloud Resource Orch
|
|
@@ -42,7 +42,7 @@ class MainOperations:
|
|
|
42
42
|
# @Todo support for all the aws policies, currently supports ec2_run as urgent requirement
|
|
43
43
|
if self._policy in policies and self._policy in ["instance_run", "unattached_volume", "cluster_run",
|
|
44
44
|
"ip_unattached", "unused_nat_gateway", "instance_idle",
|
|
45
|
-
"zombie_snapshots", "database_idle", "s3_inactive",
|
|
45
|
+
"zombie_snapshots", "database_idle", "s3_inactive", "unused_access_key",
|
|
46
46
|
"empty_roles", "tag_resources", "cost_usage_reports"]:
|
|
47
47
|
source = policy_type
|
|
48
48
|
if Utils.equal_ignore_case(policy_type, self._public_cloud_name):
|
|
@@ -37,7 +37,8 @@ class MonthlyReport:
|
|
|
37
37
|
'ip_unattached': 'Delete all the elastic_ips that are unused',
|
|
38
38
|
'unused_nat_gateway': ' Delete all unused nat gateways',
|
|
39
39
|
'zombie_snapshots': 'Delete all the snapshots which the AMI does not use',
|
|
40
|
-
's3_inactive': 'Delete the empty buckets which don’t have any content
|
|
40
|
+
's3_inactive': 'Delete the empty buckets which don’t have any content',
|
|
41
|
+
'unused_access_key': 'Deactivate user access keys that are still active but have not been used',
|
|
41
42
|
'empty_roles': 'Delete the empty role which does\'t have any policies',
|
|
42
43
|
'zombie_cluster_resource': 'Delete up the cluster resources which are not deleted while cleaning the cluster'
|
|
43
44
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from cloud_governance.common.utils.configs import UNUSED_ACCESS_KEY_DAYS, UNUSED_ACCESS_KEY_MAX_DAY
|
|
2
|
+
from cloud_governance.policy.helpers.aws.aws_policy_operations import AWSPolicyOperations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class UnusedAccessKey(AWSPolicyOperations):
|
|
6
|
+
RESOURCE_ACTION = "DeActivate"
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
super().__init__()
|
|
10
|
+
|
|
11
|
+
def run_policy_operations(self):
|
|
12
|
+
"""
|
|
13
|
+
This method returns a list of users with at least one active access key whose last used date is greater than UNUSED_ACCESS_KEY_DAYS
|
|
14
|
+
:return:
|
|
15
|
+
:rtype:
|
|
16
|
+
"""
|
|
17
|
+
unused_access_keys = []
|
|
18
|
+
iam_users_access_keys = self._get_iam_users_access_keys()
|
|
19
|
+
for username, user_data in iam_users_access_keys.items():
|
|
20
|
+
for access_key_label, access_key_data in user_data.items():
|
|
21
|
+
if 'access key' in access_key_label.lower():
|
|
22
|
+
last_activity_days = access_key_data['last_activity_days']
|
|
23
|
+
age_days = access_key_data['age_days']
|
|
24
|
+
# if access key last_activity_days is "N/A", use age_days
|
|
25
|
+
if last_activity_days == "N/A":
|
|
26
|
+
last_activity_days = age_days
|
|
27
|
+
region = user_data['region']
|
|
28
|
+
user_name = username
|
|
29
|
+
tags = user_data.get('Tags', [])
|
|
30
|
+
cleanup_result = False
|
|
31
|
+
cleanup_days = 0
|
|
32
|
+
if int(last_activity_days) >= UNUSED_ACCESS_KEY_DAYS and self._has_active_access_keys(user_name, access_key_label) and self.get_skip_policy_value(tags=tags) not in ('NOTDELETE', 'SKIP'):
|
|
33
|
+
cleanup_days = self.get_clean_up_days_count(tags=tags)
|
|
34
|
+
cleanup_result = self.verify_and_delete_resource(resource_id=user_name, tags=tags,
|
|
35
|
+
clean_up_days=cleanup_days, access_key_label=access_key_label)
|
|
36
|
+
resource_data = self._get_es_schema(resource_id=user_name,
|
|
37
|
+
user=self.get_tag_name_from_tags(tags=tags, tag_name='User'),
|
|
38
|
+
skip_policy=self.get_skip_policy_value(tags=tags),
|
|
39
|
+
cleanup_days=cleanup_days,
|
|
40
|
+
dry_run=self._dry_run,
|
|
41
|
+
name=user_name,
|
|
42
|
+
region=region,
|
|
43
|
+
cleanup_result=str(cleanup_result),
|
|
44
|
+
resource_action=self.RESOURCE_ACTION,
|
|
45
|
+
cloud_name=self._cloud_name,
|
|
46
|
+
resource_type='UnusedAccessKey',
|
|
47
|
+
resource_state='Active',
|
|
48
|
+
age_days=age_days,
|
|
49
|
+
last_activity_days=last_activity_days,
|
|
50
|
+
unit_price=0)
|
|
51
|
+
unused_access_keys.append(resource_data)
|
|
52
|
+
if not cleanup_result:
|
|
53
|
+
self.update_resource_day_count_tag(resource_id=user_name, cleanup_days=cleanup_days, tags=tags)
|
|
54
|
+
|
|
55
|
+
return unused_access_keys
|
|
@@ -20,6 +20,7 @@ class SendAggregatedAlerts:
|
|
|
20
20
|
self.__mail_to = self.__environment_variables.get('EMAIL_TO') # testing purposes
|
|
21
21
|
self.__mail_cc = self.__environment_variables.get('EMAIL_CC', [])
|
|
22
22
|
self.__alert_dry_run = self.__environment_variables.get('ALERT_DRY_RUN')
|
|
23
|
+
self.__skip_policies_alert = self.__environment_variables.get('SKIP_POLICIES_ALERT')
|
|
23
24
|
self.__mail_message = MailMessage()
|
|
24
25
|
self.__postfix = Postfix()
|
|
25
26
|
self.__es_operations = ElasticSearchOperations()
|
|
@@ -55,7 +56,7 @@ class SendAggregatedAlerts:
|
|
|
55
56
|
"ebs_in_use",
|
|
56
57
|
"instance_run", "cluster_run", "optimize_resource_report",
|
|
57
58
|
"optimize_resources_report", "skipped_resources"
|
|
58
|
-
]
|
|
59
|
+
] + self.__skip_policies_alert
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
],
|
|
@@ -162,7 +163,8 @@ class SendAggregatedAlerts:
|
|
|
162
163
|
if days >= days_to_take_action:
|
|
163
164
|
delete_date = datetime.utcnow().date().__str__()
|
|
164
165
|
alert_user = True
|
|
165
|
-
|
|
166
|
+
# Cross region policies
|
|
167
|
+
if record.get('policy') in ['empty_roles', 's3_inactive', 'unused_access_key']:
|
|
166
168
|
record['RegionName'] = 'us-east-1'
|
|
167
169
|
if Utils.equal_ignore_case(dry_run, 'yes'):
|
|
168
170
|
record['DeleteDate'] = 'dry_run=yes'
|
|
@@ -111,7 +111,7 @@ class AbstractPolicyOperations(ABC):
|
|
|
111
111
|
return 'NA'
|
|
112
112
|
|
|
113
113
|
@abstractmethod
|
|
114
|
-
def _delete_resource(self, resource_id: str):
|
|
114
|
+
def _delete_resource(self, resource_id: str, **kwargs):
|
|
115
115
|
"""
|
|
116
116
|
This method deletes the resource
|
|
117
117
|
:param resource_id:
|
|
@@ -144,7 +144,7 @@ class AbstractPolicyOperations(ABC):
|
|
|
144
144
|
:rtype:
|
|
145
145
|
"""
|
|
146
146
|
if self._resource_id == resource_id and self._force_delete and self._dry_run == 'no':
|
|
147
|
-
self._delete_resource(resource_id=resource_id)
|
|
147
|
+
self._delete_resource(resource_id=resource_id, **kwargs)
|
|
148
148
|
return True
|
|
149
149
|
if not days_to_delete_resource:
|
|
150
150
|
days_to_delete_resource = self._days_to_take_action
|
|
@@ -157,7 +157,7 @@ class AbstractPolicyOperations(ABC):
|
|
|
157
157
|
if clean_up_days >= days_to_delete_resource:
|
|
158
158
|
if self._dry_run == 'no':
|
|
159
159
|
if self.get_skip_policy_value(tags=tags) not in ('NOTDELETE', 'SKIP'):
|
|
160
|
-
self._delete_resource(resource_id=resource_id)
|
|
160
|
+
self._delete_resource(resource_id=resource_id, **kwargs)
|
|
161
161
|
cleanup_resources = True
|
|
162
162
|
return cleanup_resources
|
|
163
163
|
|
|
@@ -21,7 +21,7 @@ class AWSPolicyOperations(AbstractPolicyOperations):
|
|
|
21
21
|
self.policy_name = self._environment_variables_dict.get('policy')
|
|
22
22
|
self._cloud_name = 'AWS'
|
|
23
23
|
self._ec2_client = get_boto3_client(client='ec2', region_name=self._region)
|
|
24
|
-
self._s3_client = get_boto3_client('s3', region_name=self._region)
|
|
24
|
+
self._s3_client = get_boto3_client(client='s3', region_name=self._region)
|
|
25
25
|
self._iam_operations = IAMOperations()
|
|
26
26
|
self._rds_operations = RDSOperations(region_name=self._region)
|
|
27
27
|
self._s3operations = S3Operations(region_name=self._region)
|
|
@@ -46,7 +46,7 @@ class AWSPolicyOperations(AbstractPolicyOperations):
|
|
|
46
46
|
return tag.get('Value').strip()
|
|
47
47
|
return ''
|
|
48
48
|
|
|
49
|
-
def _delete_resource(self, resource_id: str):
|
|
49
|
+
def _delete_resource(self, resource_id: str, **kwargs):
|
|
50
50
|
"""
|
|
51
51
|
This method deletes the resource by verifying the policy
|
|
52
52
|
:param resource_id:
|
|
@@ -58,6 +58,8 @@ class AWSPolicyOperations(AbstractPolicyOperations):
|
|
|
58
58
|
try:
|
|
59
59
|
if self._policy == 's3_inactive':
|
|
60
60
|
self._s3_client.delete_bucket(Bucket=resource_id)
|
|
61
|
+
elif self._policy == 'unused_access_key':
|
|
62
|
+
self._iam_operations.deactivate_user_access_key(username=resource_id, **kwargs)
|
|
61
63
|
elif self._policy == 'empty_roles':
|
|
62
64
|
response = self._iam_operations.delete_role(role_name=resource_id)
|
|
63
65
|
elif self._policy == 'unattached_volume':
|
|
@@ -147,6 +149,8 @@ class AWSPolicyOperations(AbstractPolicyOperations):
|
|
|
147
149
|
try:
|
|
148
150
|
if self._policy == 's3_inactive':
|
|
149
151
|
self._s3_client.put_bucket_tagging(Bucket=resource_id, Tagging={'TagSet': tags})
|
|
152
|
+
elif self._policy == 'unused_access_key':
|
|
153
|
+
self._iam_operations.tag_user(user_name=resource_id, tags=tags)
|
|
150
154
|
elif self._policy == 'empty_roles':
|
|
151
155
|
self._iam_operations.tag_role(role_name=resource_id, tags=tags)
|
|
152
156
|
elif self._policy in ('ip_unattached', 'unused_nat_gateway', 'zombie_snapshots', 'unattached_volume',
|
|
@@ -196,6 +200,29 @@ class AWSPolicyOperations(AbstractPolicyOperations):
|
|
|
196
200
|
volumes = self._ec2_operations.get_volumes(**kwargs)
|
|
197
201
|
return volumes
|
|
198
202
|
|
|
203
|
+
def _get_iam_users_access_keys(self) -> dict:
|
|
204
|
+
"""
|
|
205
|
+
This method returns a list of user access keys with their age in days, last used time in days, user tags, and more.
|
|
206
|
+
:return: list of user access keys
|
|
207
|
+
"""
|
|
208
|
+
return self._iam_operations.get_iam_users_access_keys()
|
|
209
|
+
|
|
210
|
+
def _has_active_access_keys(self, user_name: str, access_key_label: str) -> bool:
|
|
211
|
+
"""
|
|
212
|
+
This method checks if the given IAM user has any active access keys.
|
|
213
|
+
:return:
|
|
214
|
+
:rtype:
|
|
215
|
+
"""
|
|
216
|
+
return self._iam_operations.has_active_access_keys(username=user_name, access_key_label=access_key_label)
|
|
217
|
+
|
|
218
|
+
def _deactivate_access_key(self, user_name: str, access_key_label: str) -> bool:
|
|
219
|
+
"""
|
|
220
|
+
This method checks if the given IAM user has any active access keys.
|
|
221
|
+
:return:
|
|
222
|
+
:rtype:
|
|
223
|
+
"""
|
|
224
|
+
return self._iam_operations.deactivate_user_access_key(username=user_name, access_key_label=access_key_label)
|
|
225
|
+
|
|
199
226
|
def _get_active_cluster_ids(self):
|
|
200
227
|
"""
|
|
201
228
|
This method returns the active cluster id's
|
cloud_governance/policy/policy_operations/aws/zombie_non_cluster/zombie_non_cluster_polices.py
CHANGED
|
@@ -45,7 +45,7 @@ class ZombieNonClusterPolicies(NonClusterZombiePolicy):
|
|
|
45
45
|
logger.error('ElasticSearch host is not pingable, Please check ')
|
|
46
46
|
|
|
47
47
|
if self._policy_output:
|
|
48
|
-
# if self._policy not in ('ec2_idle', 'ebs_in_use', 'ec2_run', 's3_inactive', 'zombie_snapshots', 'nat_gateway_unused'):
|
|
48
|
+
# if self._policy not in ('ec2_idle', 'ebs_in_use', 'ec2_run', 's3_inactive', 'unused_access_key', 'zombie_snapshots', 'nat_gateway_unused'):
|
|
49
49
|
# beautify_data = self._beautify_upload_data(upload_resource_data=response)
|
|
50
50
|
# policy_result = {'count': len(beautify_data), self._policy: beautify_data}
|
|
51
51
|
logger.info(policy_result)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloud-governance
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.371
|
|
4
4
|
Summary: Cloud Governance Tool
|
|
5
5
|
Home-page: https://github.com/redhat-performance/cloud-governance
|
|
6
6
|
Author: Red Hat
|
|
@@ -103,6 +103,7 @@ List of Policies:
|
|
|
103
103
|
- zombie_snapshots
|
|
104
104
|
- unused_nat_gateway
|
|
105
105
|
- s3_inactive
|
|
106
|
+
- unused_access_key
|
|
106
107
|
- empty_roles
|
|
107
108
|
- tag_resources
|
|
108
109
|
- tag_iam_user
|
|
@@ -51,7 +51,7 @@ cloud_governance/common/clouds/aws/dynamodb/dynamodb_operations.py,sha256=eeV3Yg
|
|
|
51
51
|
cloud_governance/common/clouds/aws/ec2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
cloud_governance/common/clouds/aws/ec2/ec2_operations.py,sha256=0ibMvf7nqyK9MCRhxrg11xnOPELqsZIQsZUgd6_neKc,24680
|
|
53
53
|
cloud_governance/common/clouds/aws/iam/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
|
-
cloud_governance/common/clouds/aws/iam/iam_operations.py,sha256=
|
|
54
|
+
cloud_governance/common/clouds/aws/iam/iam_operations.py,sha256=QXRevArr3OjSOIDhAAc61iZyZZSMGxI97ezySOgdqv8,11485
|
|
55
55
|
cloud_governance/common/clouds/aws/price/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
cloud_governance/common/clouds/aws/price/price.py,sha256=Ju76WniNTVujQPi6QFRgLt6Jd8gsxL2FOnN7c1et5F0,6403
|
|
57
57
|
cloud_governance/common/clouds/aws/price/resources_pricing.py,sha256=k-hucUvxpZfF126wLQQwJt3BzNQiOoSU1x6gjIQ_4cM,6253
|
|
@@ -111,7 +111,7 @@ cloud_governance/common/elasticsearch/elasticsearch_exceptions.py,sha256=6rwxbRY
|
|
|
111
111
|
cloud_governance/common/elasticsearch/elasticsearch_operations.py,sha256=WsAAjjCh-vX6K25SzQRP-rvPJ59RJ4dAtcXHCtmRUOQ,16529
|
|
112
112
|
cloud_governance/common/elasticsearch/modals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
113
113
|
cloud_governance/common/elasticsearch/modals/cost_usage_reports_data.py,sha256=xDxGIxJwhOtSh2VOnYx7XShRF90WPPlaQrfVm3fLQr8,1396
|
|
114
|
-
cloud_governance/common/elasticsearch/modals/policy_es_data.py,sha256=
|
|
114
|
+
cloud_governance/common/elasticsearch/modals/policy_es_data.py,sha256=ZyjHakWQRs3JeL-apxv8fMP9GhK5bR-HvR_KzSlAc5s,1492
|
|
115
115
|
cloud_governance/common/google_drive/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
116
|
cloud_governance/common/google_drive/gcp_operations.py,sha256=WtEILQlD1XQCGx93Yuwgf1HyS90ZzS_6z1Nu4aKDEvg,1137
|
|
117
117
|
cloud_governance/common/google_drive/google_drive_operations.py,sha256=U1JWNWusVlsm8xd81UF9Xgc0ZONyV4ENCkmN_W20DCw,8461
|
|
@@ -139,18 +139,18 @@ cloud_governance/common/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
139
139
|
cloud_governance/common/tool/tool.py,sha256=EG4MGXzg6k6kLFff_pdghD--QBykgAZh0OPA7g8EPog,1241
|
|
140
140
|
cloud_governance/common/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
141
141
|
cloud_governance/common/utils/api_requests.py,sha256=WwkvVAhJKEpU0JWz0Df8yN2W7iN7Q7tieJM813_DZ9c,674
|
|
142
|
-
cloud_governance/common/utils/configs.py,sha256=
|
|
142
|
+
cloud_governance/common/utils/configs.py,sha256=shFxWt0Kc-GwzcZKYCkHm058ujwdTxn42mMBcXO0egE,1255
|
|
143
143
|
cloud_governance/common/utils/json_datetime_encoder.py,sha256=_-jzRTe0UqAKTn2E9qaU8SYIxHUoRA5ElWuVA0Y54Xw,338
|
|
144
144
|
cloud_governance/common/utils/utils.py,sha256=ZUsi4ax2XhDIV-EQ5kJt5Ppd72kmm2psqcg1cNDZrvc,4349
|
|
145
145
|
cloud_governance/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
146
|
-
cloud_governance/main/environment_variables.py,sha256=
|
|
146
|
+
cloud_governance/main/environment_variables.py,sha256=y5oSVmTOaX1KyrHbbdH0RVSPPVmEsa9JtQwUzjp1RPU,29335
|
|
147
147
|
cloud_governance/main/environment_variables_exceptions.py,sha256=UR0Ith0P0oshsDZdJRlRq8ZUTt0h8jFvUtrnP4m4AIY,437
|
|
148
148
|
cloud_governance/main/es_uploader.py,sha256=6Ify5CS2NtUF1xXZ-rMwpYxVzDKfEZhv2vogWFltt98,10656
|
|
149
149
|
cloud_governance/main/main.py,sha256=4TBiCBO_1z8KpGXb8brPKBmv3jHBl8fl_-Sb80ctYno,18880
|
|
150
150
|
cloud_governance/main/main_common_operations.py,sha256=YbBJF6Smk3YKhEibnn-fIWu1oKP0pSGIM1WnSXFcBuo,366
|
|
151
151
|
cloud_governance/main/run_cloud_resource_orchestration.py,sha256=Jo7-KDqxrIo8uioyTFCohUHse4uqdEt2ZFnHlX2u57g,776
|
|
152
152
|
cloud_governance/main/main_oerations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
153
|
-
cloud_governance/main/main_oerations/main_operations.py,sha256=
|
|
153
|
+
cloud_governance/main/main_oerations/main_operations.py,sha256=iHyUDly2p0fz_gWbpeVNfiqO1f0xk09UKlnFa6sQa8U,2526
|
|
154
154
|
cloud_governance/policy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
155
155
|
cloud_governance/policy/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
156
156
|
cloud_governance/policy/aws/cost_billing_reports.py,sha256=pGzCl-BRWU5ZTM-tn34munzmetODCyeT7EtuM7XCO4A,7429
|
|
@@ -161,11 +161,12 @@ cloud_governance/policy/aws/ebs_in_use.py,sha256=7vGV2qobV14rzC7tJnJiafC3oAhnY_T
|
|
|
161
161
|
cloud_governance/policy/aws/ec2_stop.py,sha256=FXQNkHmiMCEw6Pz6CalIIVYFRsV24RT5TewqFazyP8M,9641
|
|
162
162
|
cloud_governance/policy/aws/empty_roles.py,sha256=EhsepFRbjU82PjJQ1hADqBM_RO1Ukr-Q4DZAz3bM4ho,3155
|
|
163
163
|
cloud_governance/policy/aws/ip_unattached.py,sha256=OnHlTIKSgyxwdAgU9jBApdvDQpzHgECjSodN6KoXWqU,3176
|
|
164
|
-
cloud_governance/policy/aws/monthly_report.py,sha256=
|
|
164
|
+
cloud_governance/policy/aws/monthly_report.py,sha256=cSeOfdU5u2BMUR1RmxTrrxpMSlOCG0oqKUeSqwdU_jY,6830
|
|
165
165
|
cloud_governance/policy/aws/optimize_resources_report.py,sha256=zG7w8KHF7Z25jxYGgDadyXp0jcxSREsRCOmQP8lZMNc,6003
|
|
166
166
|
cloud_governance/policy/aws/s3_inactive.py,sha256=NXUUGtJhpqohaWYczSebw-q0Q7RKg3XMMj_h7xfGhQ0,2839
|
|
167
167
|
cloud_governance/policy/aws/skipped_resources.py,sha256=D0kbt9dg6Bkl5PgGaqimWvLct6D-JOnerzJ0FkWzGFc,5679
|
|
168
168
|
cloud_governance/policy/aws/spot_savings_analysis.py,sha256=lGG5qtz8pr7xjLo5BrtVSHGTz928MLwYPbSCaDfpTes,5513
|
|
169
|
+
cloud_governance/policy/aws/unused_access_key.py,sha256=62_JaQMZLgnmYck0Brp8CTS1CvyRhwcNTXYmFjgWMoY,3542
|
|
169
170
|
cloud_governance/policy/aws/zombie_cluster_resource.py,sha256=pwE8MypeqQfSQs--U1hbk5nk1M6x0R_NLs0x-wmOIjI,59626
|
|
170
171
|
cloud_governance/policy/aws/zombie_snapshots.py,sha256=V48cq4GCG2z-MRwUSE4b5wQcGeI_T1Ah99SiHlTwkvY,3834
|
|
171
172
|
cloud_governance/policy/aws/cleanup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -186,14 +187,14 @@ cloud_governance/policy/azure/cleanup/unattached_volume.py,sha256=MXX_R7bWAYy-Yj
|
|
|
186
187
|
cloud_governance/policy/azure/cleanup/unused_nat_gateway.py,sha256=aJVYHtERfp23kiI0VAkqqu2tDzZzyYZMokrJFfUVpuw,3882
|
|
187
188
|
cloud_governance/policy/common_policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
188
189
|
cloud_governance/policy/common_policies/cloudability_cost_reports.py,sha256=ivmYvkobi8O4kYVuZNzGTJSqTenqdwb3VOc4Nv2bfh0,9540
|
|
189
|
-
cloud_governance/policy/common_policies/send_aggregated_alerts.py,sha256=
|
|
190
|
+
cloud_governance/policy/common_policies/send_aggregated_alerts.py,sha256=ejGGQCpi2NJ5qC0c9kzdrKzcLD5H5Ogc6nL8UYs6yS8,8933
|
|
190
191
|
cloud_governance/policy/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
191
192
|
cloud_governance/policy/gcp/cost_billing_reports.py,sha256=6de0r9I-Yr9GpEqHJxYCAdTUkmgNrzS-7ZthcVBL1m0,14908
|
|
192
193
|
cloud_governance/policy/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
193
|
-
cloud_governance/policy/helpers/abstract_policy_operations.py,sha256=
|
|
194
|
+
cloud_governance/policy/helpers/abstract_policy_operations.py,sha256=0Fr3OzeI5PA_D7hLCy-mSHhMyogCKX892yh32lbieGk,10690
|
|
194
195
|
cloud_governance/policy/helpers/cloudability_operations.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
195
196
|
cloud_governance/policy/helpers/aws/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
196
|
-
cloud_governance/policy/helpers/aws/aws_policy_operations.py,sha256=
|
|
197
|
+
cloud_governance/policy/helpers/aws/aws_policy_operations.py,sha256=t_-t15U7xcz-jRobgvXTX_eDQM-13F57PjEb5W2YFpU,16819
|
|
197
198
|
cloud_governance/policy/helpers/azure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
198
199
|
cloud_governance/policy/helpers/azure/azure_policy_operations.py,sha256=9V8_oU3ZnkGdwv-zDzoJZUdG0d_ZrrH4t_VmDREXhaE,10479
|
|
199
200
|
cloud_governance/policy/ibm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -236,7 +237,7 @@ cloud_governance/policy/policy_operations/aws/zombie_cluster/validate_zombies.py
|
|
|
236
237
|
cloud_governance/policy/policy_operations/aws/zombie_cluster/zombie_cluster_common_methods.py,sha256=VqXk8A0OLZOxBm422kb-n5zfM8DY4FYdkwZO91xwRxQ,14791
|
|
237
238
|
cloud_governance/policy/policy_operations/aws/zombie_non_cluster/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
238
239
|
cloud_governance/policy/policy_operations/aws/zombie_non_cluster/run_zombie_non_cluster_policies.py,sha256=jWWCFG7b3JEc0vcoeIHfiJqi1ptkLeNtVWN40XUsWGw,21045
|
|
239
|
-
cloud_governance/policy/policy_operations/aws/zombie_non_cluster/zombie_non_cluster_polices.py,sha256=
|
|
240
|
+
cloud_governance/policy/policy_operations/aws/zombie_non_cluster/zombie_non_cluster_polices.py,sha256=EmRteD5_K4JwHHPC1j25lchgKOb3gnrY4Ge9c6xlqFk,2950
|
|
240
241
|
cloud_governance/policy/policy_operations/azure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
241
242
|
cloud_governance/policy/policy_operations/azure/azure_policy_runner.py,sha256=sHvdVuZCY-FlIwe843aBtx99aC6gGNXM7r6s7Uv3xWk,1129
|
|
242
243
|
cloud_governance/policy/policy_operations/gcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -263,8 +264,8 @@ cloud_governance/policy/policy_runners/elasticsearch/__init__.py,sha256=47DEQpj8
|
|
|
263
264
|
cloud_governance/policy/policy_runners/elasticsearch/upload_elastic_search.py,sha256=pOwUJWXjJbyTy8iv3Ap8xJGnqQe-5lZgoR8-vGfAVos,1881
|
|
264
265
|
cloud_governance/policy/policy_runners/ibm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
265
266
|
cloud_governance/policy/policy_runners/ibm/policy_runner.py,sha256=V0E_f7F3hXit0aSq4BlfX1Jd4vjR2NEvOWsJ5upvZ4o,1302
|
|
266
|
-
cloud_governance-1.1.
|
|
267
|
-
cloud_governance-1.1.
|
|
268
|
-
cloud_governance-1.1.
|
|
269
|
-
cloud_governance-1.1.
|
|
270
|
-
cloud_governance-1.1.
|
|
267
|
+
cloud_governance-1.1.371.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
268
|
+
cloud_governance-1.1.371.dist-info/METADATA,sha256=hUdANpPDEvFa8C4hYH4DQCeFBg2pyN7wi0FP8Hv4yWc,11384
|
|
269
|
+
cloud_governance-1.1.371.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
270
|
+
cloud_governance-1.1.371.dist-info/top_level.txt,sha256=jfB1fgj7jvx3YZkZA4G6hFeS1RHO7J7XtnbjuMNMRww,17
|
|
271
|
+
cloud_governance-1.1.371.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|