runbooks 0.2.3__py3-none-any.whl → 0.6.1__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.
Files changed (221) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env.template +2 -0
  3. jupyter-agent/.gitattributes +35 -0
  4. jupyter-agent/README.md +16 -0
  5. jupyter-agent/app.py +256 -0
  6. jupyter-agent/cloudops-agent.png +0 -0
  7. jupyter-agent/ds-system-prompt.txt +154 -0
  8. jupyter-agent/jupyter-agent.png +0 -0
  9. jupyter-agent/llama3_template.jinja +123 -0
  10. jupyter-agent/requirements.txt +9 -0
  11. jupyter-agent/utils.py +409 -0
  12. runbooks/__init__.py +71 -3
  13. runbooks/__main__.py +13 -0
  14. runbooks/aws/ec2_describe_instances.py +1 -1
  15. runbooks/aws/ec2_run_instances.py +8 -2
  16. runbooks/aws/ec2_start_stop_instances.py +17 -4
  17. runbooks/aws/ec2_unused_volumes.py +5 -1
  18. runbooks/aws/s3_create_bucket.py +4 -2
  19. runbooks/aws/s3_list_objects.py +6 -1
  20. runbooks/aws/tagging_lambda_handler.py +13 -2
  21. runbooks/aws/tags.json +12 -0
  22. runbooks/base.py +353 -0
  23. runbooks/cfat/README.md +49 -0
  24. runbooks/cfat/__init__.py +74 -0
  25. runbooks/cfat/app.ts +644 -0
  26. runbooks/cfat/assessment/__init__.py +40 -0
  27. runbooks/cfat/assessment/asana-import.csv +39 -0
  28. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  29. runbooks/cfat/assessment/cfat.txt +520 -0
  30. runbooks/cfat/assessment/collectors.py +200 -0
  31. runbooks/cfat/assessment/jira-import.csv +39 -0
  32. runbooks/cfat/assessment/runner.py +387 -0
  33. runbooks/cfat/assessment/validators.py +290 -0
  34. runbooks/cfat/cli.py +103 -0
  35. runbooks/cfat/docs/asana-import.csv +24 -0
  36. runbooks/cfat/docs/cfat-checks.csv +31 -0
  37. runbooks/cfat/docs/cfat.txt +335 -0
  38. runbooks/cfat/docs/checks-output.png +0 -0
  39. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  40. runbooks/cfat/docs/cloudshell-download.png +0 -0
  41. runbooks/cfat/docs/cloudshell-output.png +0 -0
  42. runbooks/cfat/docs/downloadfile.png +0 -0
  43. runbooks/cfat/docs/jira-import.csv +24 -0
  44. runbooks/cfat/docs/open-cloudshell.png +0 -0
  45. runbooks/cfat/docs/report-header.png +0 -0
  46. runbooks/cfat/models.py +1026 -0
  47. runbooks/cfat/package-lock.json +5116 -0
  48. runbooks/cfat/package.json +38 -0
  49. runbooks/cfat/report.py +496 -0
  50. runbooks/cfat/reporting/__init__.py +46 -0
  51. runbooks/cfat/reporting/exporters.py +337 -0
  52. runbooks/cfat/reporting/formatters.py +496 -0
  53. runbooks/cfat/reporting/templates.py +135 -0
  54. runbooks/cfat/run-assessment.sh +23 -0
  55. runbooks/cfat/runner.py +69 -0
  56. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  57. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  58. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  59. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  60. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  61. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  62. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  63. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  64. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  65. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  66. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  67. runbooks/cfat/src/actions/create-report.ts +616 -0
  68. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  69. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  70. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  71. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  72. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  73. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  74. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  75. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  76. runbooks/cfat/src/actions/get-regions.ts +22 -0
  77. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  78. runbooks/cfat/src/types/index.d.ts +147 -0
  79. runbooks/cfat/tests/__init__.py +141 -0
  80. runbooks/cfat/tests/test_cli.py +340 -0
  81. runbooks/cfat/tests/test_integration.py +290 -0
  82. runbooks/cfat/tests/test_models.py +505 -0
  83. runbooks/cfat/tests/test_reporting.py +354 -0
  84. runbooks/cfat/tsconfig.json +16 -0
  85. runbooks/cfat/webpack.config.cjs +27 -0
  86. runbooks/config.py +260 -0
  87. runbooks/finops/__init__.py +88 -0
  88. runbooks/finops/aws_client.py +245 -0
  89. runbooks/finops/cli.py +151 -0
  90. runbooks/finops/cost_processor.py +410 -0
  91. runbooks/finops/dashboard_runner.py +448 -0
  92. runbooks/finops/helpers.py +355 -0
  93. runbooks/finops/main.py +14 -0
  94. runbooks/finops/profile_processor.py +174 -0
  95. runbooks/finops/types.py +66 -0
  96. runbooks/finops/visualisations.py +80 -0
  97. runbooks/inventory/.gitignore +354 -0
  98. runbooks/inventory/ArgumentsClass.py +261 -0
  99. runbooks/inventory/Inventory_Modules.py +6130 -0
  100. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  101. runbooks/inventory/README.md +1320 -0
  102. runbooks/inventory/__init__.py +62 -0
  103. runbooks/inventory/account_class.py +532 -0
  104. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  105. runbooks/inventory/aws_decorators.py +201 -0
  106. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  107. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  108. runbooks/inventory/check_controltower_readiness.py +1107 -0
  109. runbooks/inventory/check_landingzone_readiness.py +711 -0
  110. runbooks/inventory/cloudtrail.md +727 -0
  111. runbooks/inventory/collectors/__init__.py +20 -0
  112. runbooks/inventory/collectors/aws_compute.py +518 -0
  113. runbooks/inventory/collectors/aws_networking.py +275 -0
  114. runbooks/inventory/collectors/base.py +222 -0
  115. runbooks/inventory/core/__init__.py +19 -0
  116. runbooks/inventory/core/collector.py +303 -0
  117. runbooks/inventory/core/formatter.py +296 -0
  118. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  119. runbooks/inventory/discovery.md +81 -0
  120. runbooks/inventory/draw_org_structure.py +748 -0
  121. runbooks/inventory/ec2_vpc_utils.py +341 -0
  122. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  123. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  124. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  125. runbooks/inventory/find_ec2_security_groups.py +669 -0
  126. runbooks/inventory/find_landingzone_versions.py +201 -0
  127. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  128. runbooks/inventory/inventory.sh +659 -0
  129. runbooks/inventory/list_cfn_stacks.py +558 -0
  130. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  131. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  132. runbooks/inventory/list_cfn_stacksets.py +453 -0
  133. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  134. runbooks/inventory/list_ds_directories.py +354 -0
  135. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  136. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  137. runbooks/inventory/list_ec2_instances.py +425 -0
  138. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  139. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  140. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  141. runbooks/inventory/list_guardduty_detectors.py +568 -0
  142. runbooks/inventory/list_iam_policies.py +404 -0
  143. runbooks/inventory/list_iam_roles.py +518 -0
  144. runbooks/inventory/list_iam_saml_providers.py +359 -0
  145. runbooks/inventory/list_lambda_functions.py +882 -0
  146. runbooks/inventory/list_org_accounts.py +446 -0
  147. runbooks/inventory/list_org_accounts_users.py +354 -0
  148. runbooks/inventory/list_rds_db_instances.py +406 -0
  149. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  150. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  151. runbooks/inventory/list_sns_topics.py +360 -0
  152. runbooks/inventory/list_ssm_parameters.py +402 -0
  153. runbooks/inventory/list_vpc_subnets.py +433 -0
  154. runbooks/inventory/list_vpcs.py +422 -0
  155. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  156. runbooks/inventory/models/__init__.py +24 -0
  157. runbooks/inventory/models/account.py +192 -0
  158. runbooks/inventory/models/inventory.py +309 -0
  159. runbooks/inventory/models/resource.py +247 -0
  160. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  161. runbooks/inventory/requirements.txt +12 -0
  162. runbooks/inventory/run_on_multi_accounts.py +211 -0
  163. runbooks/inventory/tests/common_test_data.py +3661 -0
  164. runbooks/inventory/tests/common_test_functions.py +204 -0
  165. runbooks/inventory/tests/script_test_data.py +0 -0
  166. runbooks/inventory/tests/setup.py +24 -0
  167. runbooks/inventory/tests/src.py +18 -0
  168. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  169. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  170. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  171. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  172. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  173. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  174. runbooks/inventory/update_aws_actions.py +173 -0
  175. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  176. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  177. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  178. runbooks/inventory/update_s3_public_access_block.py +539 -0
  179. runbooks/inventory/utils/__init__.py +23 -0
  180. runbooks/inventory/utils/aws_helpers.py +510 -0
  181. runbooks/inventory/utils/threading_utils.py +493 -0
  182. runbooks/inventory/utils/validation.py +682 -0
  183. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  184. runbooks/main.py +785 -0
  185. runbooks/organizations/__init__.py +12 -0
  186. runbooks/organizations/manager.py +374 -0
  187. runbooks/security_baseline/README.md +324 -0
  188. runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
  189. runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
  190. runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
  191. runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
  192. runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
  193. runbooks/security_baseline/checklist/root_access_key.py +6 -1
  194. runbooks/security_baseline/config-origin.json +1 -1
  195. runbooks/security_baseline/config.json +1 -1
  196. runbooks/security_baseline/permission.json +1 -1
  197. runbooks/security_baseline/report_generator.py +10 -2
  198. runbooks/security_baseline/report_template_en.html +8 -8
  199. runbooks/security_baseline/report_template_jp.html +8 -8
  200. runbooks/security_baseline/report_template_kr.html +13 -13
  201. runbooks/security_baseline/report_template_vn.html +8 -8
  202. runbooks/security_baseline/requirements.txt +7 -0
  203. runbooks/security_baseline/run_script.py +8 -2
  204. runbooks/security_baseline/security_baseline_tester.py +10 -2
  205. runbooks/security_baseline/utils/common.py +5 -1
  206. runbooks/utils/__init__.py +204 -0
  207. runbooks-0.6.1.dist-info/METADATA +373 -0
  208. runbooks-0.6.1.dist-info/RECORD +237 -0
  209. {runbooks-0.2.3.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
  210. runbooks-0.6.1.dist-info/entry_points.txt +7 -0
  211. runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
  212. runbooks-0.6.1.dist-info/top_level.txt +3 -0
  213. runbooks/python101/calculator.py +0 -34
  214. runbooks/python101/config.py +0 -1
  215. runbooks/python101/exceptions.py +0 -16
  216. runbooks/python101/file_manager.py +0 -218
  217. runbooks/python101/toolkit.py +0 -153
  218. runbooks-0.2.3.dist-info/METADATA +0 -435
  219. runbooks-0.2.3.dist-info/RECORD +0 -61
  220. runbooks-0.2.3.dist-info/entry_points.txt +0 -3
  221. runbooks-0.2.3.dist-info/top_level.txt +0 -1
@@ -24,19 +24,19 @@
24
24
  </path>
25
25
  </symbol>
26
26
  <symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
27
- <path
27
+ <path
28
28
  d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
29
29
  </path>
30
30
  </symbol>
31
31
  <symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
32
- <path
32
+ <path
33
33
  d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
34
34
  </path>
35
- <path
35
+ <path
36
36
  d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
37
37
  </path>
38
38
  </symbol>
39
- </svg>
39
+ </svg>
40
40
 
41
41
  <div class="container">
42
42
  <div class="row justify-content-center">
@@ -49,7 +49,7 @@
49
49
  <div class="col-6">
50
50
  <table class="table">
51
51
  <tr>
52
- <td>Account</td><td>{{ account_id }}</td>
52
+ <td>AWS Account</td><td>{{ account_id }}</td>
53
53
  </tr>
54
54
  <tr>
55
55
  <td>Generated at</td><td>{{ generated_at }}</td>
@@ -88,15 +88,15 @@
88
88
  {% for section in result_sections %}
89
89
  <div class="card-body" id="{{ section.level|lower }}-list">
90
90
  <div class="accordion" id="accordionSection{{ section.level }}">
91
-
91
+
92
92
  {% for item in section.result_items %}
93
93
  <div class="accordion-item">
94
94
  <h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
95
95
  <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
96
96
  <span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
97
- <div class="ms-2 me-auto">{{ item.title }}</div>
97
+ <div class="ms-2 me-auto">{{ item.title }}</div>
98
98
  </button>
99
- </h2>
99
+ </h2>
100
100
  <div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
101
101
  <div class="accordion-body">
102
102
  <div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
@@ -141,7 +141,7 @@
141
141
  <div class="col-10 col-lg-8">
142
142
 
143
143
  <!-- Below this section, you can add content references for each language. -->
144
-
144
+
145
145
  <br>
146
146
  <div class="row">
147
147
  <div class="alert alert-primary d-flex align-items-center" role="alert">
@@ -156,7 +156,7 @@
156
156
  </span>
157
157
  </div>
158
158
  </div>
159
-
159
+
160
160
  <div class="row">
161
161
  <div class="alert alert-primary d-flex align-items-center" role="alert">
162
162
  <svg aria-label="Info:" class="bi flex-shrink-0 me-2" width="24" height="24" role="img">
@@ -169,21 +169,21 @@
169
169
  </span>
170
170
  </div>
171
171
  </div>
172
-
172
+
173
173
  <div class="row">
174
174
  <div class="alert alert-primary d-flex align-items-center" role="alert">
175
175
  <svg aria-label="Info:" class="bi flex-shrink-0 me-2" width="24" height="24" role="img">
176
176
  <use xlink:href="#info-fill"></use>
177
177
  </svg>
178
178
  <span>
179
- AWS Trusted Advisor 사용 방법에 대한 자세한 내용은
179
+ AWS Trusted Advisor 사용 방법에 대한 자세한 내용은
180
180
  <a href="https://aws.amazon.com/ko/premiumsupport/knowledge-center/trusted-advisor-intro/" target="_blank" style="overflow:hidden;word-break:break-all;">
181
181
  여기
182
182
  </a>를 눌러 확인해주세요.
183
183
  </span>
184
184
  </div>
185
185
  </div>
186
-
186
+
187
187
  <div class="col"></div>
188
188
  </div>
189
189
  </div>
@@ -24,19 +24,19 @@
24
24
  </path>
25
25
  </symbol>
26
26
  <symbol id="bi-x-circle-fill" fill="currentColor" viewBox="0 0 16 16">
27
- <path
27
+ <path
28
28
  d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z">
29
29
  </path>
30
30
  </symbol>
31
31
  <symbol id="bi-dash-circle" fill="currentColor" viewBox="0 0 16 16">
32
- <path
32
+ <path
33
33
  d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z">
34
34
  </path>
35
- <path
35
+ <path
36
36
  d="M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8z">
37
37
  </path>
38
38
  </symbol>
39
- </svg>
39
+ </svg>
40
40
 
41
41
  <div class="container">
42
42
  <div class="row justify-content-center">
@@ -49,7 +49,7 @@
49
49
  <div class="col-6">
50
50
  <table class="table">
51
51
  <tr>
52
- <td>Account</td><td>{{ account_id }}</td>
52
+ <td>AWS Account</td><td>{{ account_id }}</td>
53
53
  </tr>
54
54
  <tr>
55
55
  <td>Generated at</td><td>{{ generated_at }}</td>
@@ -88,15 +88,15 @@
88
88
  {% for section in result_sections %}
89
89
  <div class="card-body" id="{{ section.level|lower }}-list">
90
90
  <div class="accordion" id="accordionSection{{ section.level }}">
91
-
91
+
92
92
  {% for item in section.result_items %}
93
93
  <div class="accordion-item">
94
94
  <h2 class="accordion-header" id="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
95
95
  <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" aria-expanded="true" aria-controls="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}">
96
96
  <span class="badge text-bg-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} rounded-pill">{{ section.level }}</span>
97
- <div class="ms-2 me-auto">{{ item.title }}</div>
97
+ <div class="ms-2 me-auto">{{ item.title }}</div>
98
98
  </button>
99
- </h2>
99
+ </h2>
100
100
  <div id="panelsStayOpen-collapse-{{ section.level|lower }}-{{ '%02d'|format(loop.index)}}" class="accordion-collapse collapse" aria-labelledby="panelsStayOpen-heading-{{ section.level|lower }}-{{ '%02d'|format(loop.index) }}">
101
101
  <div class="accordion-body">
102
102
  <div class="alert alert-{% if section.level == 'Error' %}dark{% elif section.level == 'Info' %}primary{% else %}{{ section.level|lower }}{% endif %} d-flex align-items-center" role="alert">
@@ -0,0 +1,7 @@
1
+ boto3==1.26.70
2
+ botocore==1.29.70
3
+ jmespath==1.0.1
4
+ python-dateutil==2.8.2
5
+ s3transfer==0.6.0
6
+ six==1.16.0
7
+ urllib3==1.26.18
@@ -37,7 +37,9 @@ def parse_arguments():
37
37
  description="AWS Security Baseline Tester - Evaluate your AWS account's security configuration."
38
38
  )
39
39
  parser.add_argument(
40
- "--profile", default="default", help="AWS IAM profile to use for authentication (default: 'default')."
40
+ "--profile",
41
+ default="default",
42
+ help="AWS IAM profile to use for authentication (default: 'default').",
41
43
  )
42
44
  parser.add_argument(
43
45
  "--language",
@@ -45,7 +47,11 @@ def parse_arguments():
45
47
  default="EN",
46
48
  help="Language for the Security Baseline Report (default: 'EN').",
47
49
  )
48
- parser.add_argument("--output", default=None, help="Custom output directory for HTML results (default: ./results).")
50
+ parser.add_argument(
51
+ "--output",
52
+ default=None,
53
+ help="Custom output directory for HTML results (default: ./results).",
54
+ )
49
55
  return parser.parse_args()
50
56
 
51
57
 
@@ -60,7 +60,10 @@ class SecurityBaselineTester:
60
60
 
61
61
  logging.info(self.translator.translate("test_completed"))
62
62
  except Exception as e:
63
- logging.error(f"An error occurred during the security baseline test: {str(e)}", exc_info=True)
63
+ logging.error(
64
+ f"An error occurred during the security baseline test: {str(e)}",
65
+ exc_info=True,
66
+ )
64
67
 
65
68
  def _validate_session(self):
66
69
  if self.session.region_name is None:
@@ -122,7 +125,12 @@ class SecurityBaselineTester:
122
125
  "iam_password_policy",
123
126
  ]:
124
127
  return check_method(self.session, translator)
125
- elif check_name in ["root_mfa", "root_usage", "root_access_key", "iam_user_mfa"]:
128
+ elif check_name in [
129
+ "root_mfa",
130
+ "root_usage",
131
+ "root_access_key",
132
+ "iam_user_mfa",
133
+ ]:
126
134
  return check_method(self.session, translator, credential_report)
127
135
  elif check_name == "trusted_advisor":
128
136
  return check_method(translator)
@@ -15,7 +15,11 @@ class Ret:
15
15
  self.headers = {"Content-Type": "text/html;charset=UTF-8"}
16
16
 
17
17
  def to_dict(self) -> dict:
18
- return {"statusCode": self.status_code, "body": self.body, "headers": self.headers}
18
+ return {
19
+ "statusCode": self.status_code,
20
+ "body": self.body,
21
+ "headers": self.headers,
22
+ }
19
23
 
20
24
 
21
25
  class CheckResult:
@@ -0,0 +1,204 @@
1
+ """
2
+ Utilities package for CloudOps Runbooks.
3
+
4
+ This package provides utility modules including logging, configuration,
5
+ and helper functions used throughout the runbooks package.
6
+ """
7
+
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ try:
13
+ from loguru import logger
14
+
15
+ _HAS_LOGURU = True
16
+ except ImportError:
17
+ import logging
18
+
19
+ logger = logging.getLogger(__name__)
20
+ _HAS_LOGURU = False
21
+
22
+ # Legacy utilities
23
+ from runbooks.utils.logger import configure_logger
24
+
25
+
26
+ def setup_logging(debug: bool = False, log_file: Optional[str] = None) -> None:
27
+ """
28
+ Configure logging for the application.
29
+
30
+ Args:
31
+ debug: Enable debug logging if True
32
+ log_file: Optional path to log file
33
+ """
34
+ if _HAS_LOGURU:
35
+ from loguru import logger as loguru_logger
36
+
37
+ # Remove default handler
38
+ loguru_logger.remove()
39
+
40
+ # Console handler with appropriate level
41
+ log_level = "DEBUG" if debug else "INFO"
42
+ loguru_logger.add(
43
+ sys.stderr,
44
+ level=log_level,
45
+ format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
46
+ colorize=True,
47
+ )
48
+
49
+ # File handler if specified
50
+ if log_file:
51
+ log_path = Path(log_file)
52
+ log_path.parent.mkdir(parents=True, exist_ok=True)
53
+
54
+ loguru_logger.add(
55
+ log_path,
56
+ level="DEBUG",
57
+ format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
58
+ rotation="10 MB",
59
+ retention="7 days",
60
+ compression="zip",
61
+ )
62
+
63
+ loguru_logger.info(f"Logging initialized with level: {log_level}")
64
+ else:
65
+ # Fallback to standard logging
66
+ import logging
67
+
68
+ log_level = logging.DEBUG if debug else logging.INFO
69
+ logging.basicConfig(
70
+ level=log_level,
71
+ format="%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d - %(message)s",
72
+ handlers=[logging.StreamHandler(sys.stderr)],
73
+ )
74
+
75
+ if log_file:
76
+ log_path = Path(log_file)
77
+ log_path.parent.mkdir(parents=True, exist_ok=True)
78
+ file_handler = logging.FileHandler(log_path)
79
+ file_handler.setFormatter(
80
+ logging.Formatter("%(asctime)s | %(levelname)-8s | %(name)s:%(funcName)s:%(lineno)d - %(message)s")
81
+ )
82
+ logging.getLogger().addHandler(file_handler)
83
+
84
+ logging.info(f"Logging initialized with level: {log_level}")
85
+
86
+
87
+ def validate_aws_profile(profile: str) -> bool:
88
+ """
89
+ Validate that an AWS profile exists in credentials.
90
+
91
+ Args:
92
+ profile: AWS profile name to validate
93
+
94
+ Returns:
95
+ True if profile exists, False otherwise
96
+ """
97
+ import configparser
98
+
99
+ try:
100
+ # Check ~/.aws/credentials
101
+ credentials_path = Path.home() / ".aws" / "credentials"
102
+ if credentials_path.exists():
103
+ config = configparser.ConfigParser()
104
+ config.read(credentials_path)
105
+ if profile in config.sections():
106
+ return True
107
+
108
+ # Check ~/.aws/config
109
+ config_path = Path.home() / ".aws" / "config"
110
+ if config_path.exists():
111
+ config = configparser.ConfigParser()
112
+ config.read(config_path)
113
+ profile_section = f"profile {profile}" if profile != "default" else "default"
114
+ if profile_section in config.sections():
115
+ return True
116
+
117
+ return False
118
+ except Exception as e:
119
+ logger.warning(f"Error validating AWS profile '{profile}': {e}")
120
+ return False
121
+
122
+
123
+ def ensure_directory(path: Path) -> Path:
124
+ """
125
+ Ensure directory exists, creating it if necessary.
126
+
127
+ Args:
128
+ path: Directory path to ensure
129
+
130
+ Returns:
131
+ The directory path
132
+ """
133
+ path.mkdir(parents=True, exist_ok=True)
134
+ return path
135
+
136
+
137
+ def format_size(size_bytes: int) -> str:
138
+ """
139
+ Format byte size in human readable format.
140
+
141
+ Args:
142
+ size_bytes: Size in bytes
143
+
144
+ Returns:
145
+ Formatted size string (e.g., "1.5 GB")
146
+ """
147
+ if size_bytes == 0:
148
+ return "0 B"
149
+
150
+ size_names = ["B", "KB", "MB", "GB", "TB", "PB"]
151
+ size_index = 0
152
+ size = float(size_bytes)
153
+
154
+ while size >= 1024 and size_index < len(size_names) - 1:
155
+ size /= 1024
156
+ size_index += 1
157
+
158
+ return f"{size:.1f} {size_names[size_index]}"
159
+
160
+
161
+ def retry_with_backoff(max_retries: int = 3, backoff_factor: float = 1.0):
162
+ """
163
+ Decorator for retrying operations with exponential backoff.
164
+
165
+ Args:
166
+ max_retries: Maximum number of retry attempts
167
+ backoff_factor: Factor for exponential backoff
168
+ """
169
+ import time
170
+ from functools import wraps
171
+
172
+ def decorator(func):
173
+ @wraps(func)
174
+ def wrapper(*args, **kwargs):
175
+ last_exception = None
176
+
177
+ for attempt in range(max_retries + 1):
178
+ try:
179
+ return func(*args, **kwargs)
180
+ except Exception as e:
181
+ last_exception = e
182
+ if attempt == max_retries:
183
+ logger.error(f"Failed after {max_retries} retries: {e}")
184
+ raise
185
+
186
+ wait_time = backoff_factor * (2**attempt)
187
+ logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying in {wait_time}s...")
188
+ time.sleep(wait_time)
189
+
190
+ raise last_exception
191
+
192
+ return wrapper
193
+
194
+ return decorator
195
+
196
+
197
+ __all__ = [
198
+ "setup_logging",
199
+ "validate_aws_profile",
200
+ "ensure_directory",
201
+ "format_size",
202
+ "retry_with_backoff",
203
+ "configure_logger",
204
+ ]