/idem_aws/exec/aws/cloudtrail/trail.py

https://gitlab.com/pgeorgiev_vmw/idem-aws
Python | 334 lines | 301 code | 22 blank | 11 comment | 64 complexity | 502493af1c01ccc89a0ed4cf3aa40c4b MD5 | raw file
  1. import copy
  2. from collections import OrderedDict
  3. from typing import Any
  4. from typing import Dict
  5. from typing import List
  6. async def update_tags(
  7. hub,
  8. ctx,
  9. resource_id,
  10. old_tags: List[Dict[str, Any]],
  11. new_tags: List[Dict[str, Any]],
  12. ):
  13. """
  14. Update tags of AWS CloudTrail resources
  15. Args:
  16. hub:
  17. ctx:
  18. resource_id: aws resource id
  19. old_tags: list of old tags
  20. new_tags: list of new tags
  21. Returns:
  22. {"result": True|False, "comment": A message Tuple, "ret": None}
  23. """
  24. tags_to_add = []
  25. tags_to_remove = []
  26. old_tags_map = {tag.get("Key"): tag for tag in old_tags or []}
  27. tags_result = copy.deepcopy(old_tags_map)
  28. if new_tags is not None:
  29. for tag in new_tags:
  30. if tag.get("Key") in old_tags_map:
  31. if tag.get("Value") != old_tags_map.get(tag.get("Key")).get("Value"):
  32. tags_to_add.append(tag)
  33. del old_tags_map[tag.get("Key")]
  34. else:
  35. tags_to_add.append(tag)
  36. tags_to_remove = [
  37. {"Key": tag.get("Key"), "Value": tag.get("Value")}
  38. for tag in old_tags_map.values()
  39. ]
  40. result = dict(comment=(), result=True, ret=None)
  41. if (not tags_to_remove) and (not tags_to_add):
  42. return result
  43. if tags_to_remove:
  44. if not ctx.get("test", False):
  45. delete_ret = await hub.exec.boto3.client.cloudtrail.remove_tags(
  46. ctx, ResourceId=resource_id, TagsList=tags_to_remove
  47. )
  48. if not delete_ret["result"]:
  49. result["comment"] = delete_ret["comment"]
  50. result["result"] = False
  51. return result
  52. [tags_result.pop(key.get("Key"), None) for key in tags_to_remove]
  53. if tags_to_add:
  54. if not ctx.get("test", False):
  55. add_ret = await hub.exec.boto3.client.cloudtrail.add_tags(
  56. ctx, ResourceId=resource_id, TagsList=tags_to_add
  57. )
  58. if not add_ret["result"]:
  59. result["comment"] = add_ret["comment"]
  60. result["result"] = False
  61. return result
  62. result["ret"] = {"tags": list(tags_result.values()) + tags_to_add}
  63. result["comment"] = (f"Update tags: Add [{tags_to_add}] Remove [{tags_to_remove}]",)
  64. return result
  65. async def update_trail(
  66. hub,
  67. ctx,
  68. before: Dict[str, Any],
  69. s3_bucket_name: str,
  70. s3_key_prefix: str,
  71. sns_topic_name: str,
  72. include_global_service_events: bool,
  73. is_multi_region_trail: bool,
  74. enable_logfile_validation: bool,
  75. cloud_watch_logs_loggroup_arn: str,
  76. cloud_watch_logs_role_arn: str,
  77. kms_key_id: str,
  78. is_organization_trail: bool,
  79. ):
  80. result = dict(comment=(), result=True, ret=None)
  81. update_payload = {}
  82. resource_parameters = OrderedDict(
  83. {
  84. "S3BucketName": s3_bucket_name,
  85. "S3KeyPrefix": s3_key_prefix,
  86. "SnsTopicName": sns_topic_name,
  87. "IncludeGlobalServiceEvents": include_global_service_events,
  88. "IsMultiRegionTrail": is_multi_region_trail,
  89. "CloudWatchLogsLogGroupArn": cloud_watch_logs_loggroup_arn,
  90. "CloudWatchLogsRoleArn": cloud_watch_logs_role_arn,
  91. "KmsKeyId": kms_key_id,
  92. "IsOrganizationTrail": is_organization_trail,
  93. }
  94. )
  95. if "LogFileValidationEnabled" in before.keys():
  96. if (
  97. enable_logfile_validation is not None
  98. ) and enable_logfile_validation != before["LogFileValidationEnabled"]:
  99. update_payload["EnableLogFileValidation"] = enable_logfile_validation
  100. for key, value in resource_parameters.items():
  101. if key in before.keys():
  102. if (value is not None) and value != before[key]:
  103. update_payload[key] = resource_parameters[key]
  104. if update_payload:
  105. if not ctx.get("test", False):
  106. update_ret = await hub.exec.boto3.client.cloudtrail.update_trail(
  107. ctx, Name=before.get("Name"), **update_payload
  108. )
  109. if not update_ret["result"]:
  110. result["comment"] = result["comment"] + update_ret["comment"]
  111. result["result"] = False
  112. return result
  113. result["ret"] = {}
  114. result = update_result(result, update_payload)
  115. return result
  116. def update_result(
  117. result: Dict[str, Any], update_payload: Dict[str, Any]
  118. ) -> Dict[str, Any]:
  119. parameters = OrderedDict(
  120. {
  121. "S3BucketName": "s3_bucket_name",
  122. "S3KeyPrefix": "s3_key_prefix",
  123. "SnsTopicName": "sns_topic_name",
  124. "IncludeGlobalServiceEvents": "include_global_service_events",
  125. "IsMultiRegionTrail": "is_multi_region_trail",
  126. "EnableLogFileValidation": "enable_logfile_validation",
  127. "CloudWatchLogsLogGroupArn": "cloud_watch_logs_loggroup_arn",
  128. "CloudWatchLogsRoleArn": "cloud_watch_logs_role_arn",
  129. "KmsKeyId": "kms_key_id",
  130. "IsOrganizationTrail": "is_organization_trail",
  131. }
  132. )
  133. for raw_parameter, present_parameter in parameters.items():
  134. if raw_parameter in update_payload:
  135. result["ret"][present_parameter] = update_payload[raw_parameter]
  136. result["comment"] = result["comment"] + (
  137. f"Update {present_parameter}: {update_payload[raw_parameter]}",
  138. )
  139. return result
  140. async def update_trail_attributes(
  141. hub,
  142. ctx,
  143. before: Dict[str, Any],
  144. resource_id: str,
  145. is_logging: bool,
  146. update_insight_selectors: List,
  147. update_event_selectors: List,
  148. update_advanced_event_selectors: List,
  149. ) -> Dict[str, Any]:
  150. result = dict(comment=(), result=True, ret=None)
  151. exising_attributes = await existing_attributes(hub, ctx, before, resource_id)
  152. result["ret"] = {}
  153. if not exising_attributes["result"]:
  154. result["comment"] = exising_attributes["comment"]
  155. result["result"] = False
  156. if (
  157. is_logging is not None
  158. and exising_attributes["result"]
  159. and is_logging != exising_attributes["ret"].get("existingTrailStatus")
  160. ):
  161. if not ctx.get("test", False):
  162. if is_logging:
  163. update_ret = await hub.exec.boto3.client.cloudtrail.start_logging(
  164. ctx, Name=before["TrailARN"]
  165. )
  166. else:
  167. update_ret = await hub.exec.boto3.client.cloudtrail.stop_logging(
  168. ctx, Name=before["TrailARN"]
  169. )
  170. if not update_ret["result"]:
  171. result["comment"] = result["comment"] + update_ret["comment"]
  172. result["result"] = False
  173. return result
  174. result["ret"]["is_logging"] = is_logging
  175. result["comment"] = result["comment"] + (f"Update is_logging: {is_logging}",)
  176. if (
  177. exising_attributes["result"]
  178. and update_insight_selectors is not None
  179. and (
  180. not hub.tool.aws.state_comparison_utils.are_lists_identical(
  181. update_insight_selectors,
  182. exising_attributes["ret"].get("existingInsightSelectors"),
  183. )
  184. )
  185. ):
  186. if not ctx.get("test", False):
  187. update_ret = await hub.exec.boto3.client.cloudtrail.put_insight_selectors(
  188. ctx,
  189. TrailName=before["TrailARN"],
  190. InsightSelectors=update_insight_selectors,
  191. )
  192. if not update_ret["result"]:
  193. result["comment"] = result["comment"] + update_ret["comment"]
  194. result["result"] = False
  195. return result
  196. result["ret"]["insight_selectors"] = update_insight_selectors
  197. result["comment"] = result["comment"] + (
  198. f"Update insight_selectors: {update_insight_selectors}",
  199. )
  200. ## Update event selectors
  201. if (
  202. exising_attributes["result"]
  203. and update_event_selectors is not None
  204. and (
  205. not hub.tool.aws.state_comparison_utils.are_lists_identical(
  206. update_event_selectors, exising_attributes["ret"].get("event_selectors")
  207. )
  208. )
  209. ):
  210. if not ctx.get("test", False):
  211. update_ret = await hub.exec.boto3.client.cloudtrail.put_event_selectors(
  212. ctx,
  213. TrailName=before["TrailARN"],
  214. EventSelectors=update_event_selectors,
  215. AdvancedEventSelectors=update_advanced_event_selectors,
  216. )
  217. if not update_ret["result"]:
  218. result["comment"] = result["comment"] + update_ret["comment"]
  219. result["result"] = False
  220. return result
  221. result["ret"]["event_selectors"] = update_event_selectors
  222. result["comment"] = result["comment"] + (
  223. f"Update event_selectors: {update_event_selectors}",
  224. )
  225. if (
  226. exising_attributes["result"]
  227. and update_advanced_event_selectors is not None
  228. and (
  229. not hub.tool.aws.state_comparison_utils.are_lists_identical(
  230. update_advanced_event_selectors,
  231. exising_attributes["ret"].get("advanced_event_selectors"),
  232. )
  233. )
  234. ):
  235. if not ctx.get("test", False):
  236. update_ret = await hub.exec.boto3.client.cloudtrail.put_event_selectors(
  237. ctx,
  238. TrailName=before["TrailARN"],
  239. EventSelectors=update_event_selectors,
  240. AdvancedEventSelectors=update_advanced_event_selectors,
  241. )
  242. if not update_ret["result"]:
  243. result["comment"] = result["comment"] + update_ret["comment"]
  244. result["result"] = False
  245. return result
  246. result["ret"]["advanced_event_selectors"] = update_advanced_event_selectors
  247. result["comment"] = result["comment"] + (
  248. f"Update advanced_event_selectors: {update_advanced_event_selectors}",
  249. )
  250. return result
  251. async def existing_attributes(
  252. hub, ctx, before: Dict[str, Any], resource_id: str
  253. ) -> Dict[str, Any]:
  254. result = dict(comment=(), result=True, ret=None)
  255. existing_logging = None
  256. existing_insight_selectors = None
  257. existing_event_selectors = None
  258. existing_advanced_event_selectors = None
  259. if not ctx.get("test", False):
  260. existingTrailStatus = await hub.exec.boto3.client.cloudtrail.get_trail_status(
  261. ctx, Name=resource_id
  262. )
  263. if existingTrailStatus["result"]:
  264. existing_logging = existingTrailStatus["ret"]["IsLogging"]
  265. else:
  266. result["comment"] = result["comment"] + existingTrailStatus["comment"]
  267. result["result"] = False
  268. if not ctx.get("test", False):
  269. existingInsightSelectors = (
  270. await hub.exec.boto3.client.cloudtrail.get_insight_selectors(
  271. ctx,
  272. TrailName=resource_id,
  273. )
  274. )
  275. if before["HasInsightSelectors"]:
  276. if existingInsightSelectors["result"]:
  277. existing_insight_selectors = existingInsightSelectors["ret"][
  278. "InsightSelectors"
  279. ]
  280. else:
  281. result["comment"] = (
  282. result["comment"] + existingInsightSelectors["comment"]
  283. )
  284. result["result"] = False
  285. if not ctx.get("test", False):
  286. existingEventSelectors = (
  287. await hub.exec.boto3.client.cloudtrail.get_event_selectors(
  288. ctx,
  289. TrailName=resource_id,
  290. )
  291. )
  292. if existingEventSelectors["result"]:
  293. if "EventSelectors" in existingEventSelectors["ret"]:
  294. existing_event_selectors = existingEventSelectors["ret"][
  295. "EventSelectors"
  296. ]
  297. else:
  298. existing_advanced_event_selectors = existingEventSelectors["ret"][
  299. "AdvancedEventSelectors"
  300. ]
  301. else:
  302. result["comment"] = result["comment"] + existingEventSelectors["comment"]
  303. result["result"] = False
  304. result["ret"] = {
  305. "existingTrailStatus": existing_logging,
  306. "existingInsightSelectors": existing_insight_selectors,
  307. "event_selectors": existing_event_selectors,
  308. "advanced_event_selectors": existing_advanced_event_selectors,
  309. }
  310. return result