runbooks 0.2.5__py3-none-any.whl → 0.7.0__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 (249) hide show
  1. conftest.py +26 -0
  2. jupyter-agent/.env +2 -0
  3. jupyter-agent/.env.template +2 -0
  4. jupyter-agent/.gitattributes +35 -0
  5. jupyter-agent/.gradio/certificate.pem +31 -0
  6. jupyter-agent/README.md +16 -0
  7. jupyter-agent/__main__.log +8 -0
  8. jupyter-agent/app.py +256 -0
  9. jupyter-agent/cloudops-agent.png +0 -0
  10. jupyter-agent/ds-system-prompt.txt +154 -0
  11. jupyter-agent/jupyter-agent.png +0 -0
  12. jupyter-agent/llama3_template.jinja +123 -0
  13. jupyter-agent/requirements.txt +9 -0
  14. jupyter-agent/tmp/4ojbs8a02ir/jupyter-agent.ipynb +68 -0
  15. jupyter-agent/tmp/cm5iasgpm3p/jupyter-agent.ipynb +91 -0
  16. jupyter-agent/tmp/crqbsseag5/jupyter-agent.ipynb +91 -0
  17. jupyter-agent/tmp/hohanq1u097/jupyter-agent.ipynb +57 -0
  18. jupyter-agent/tmp/jns1sam29wm/jupyter-agent.ipynb +53 -0
  19. jupyter-agent/tmp/jupyter-agent.ipynb +27 -0
  20. jupyter-agent/utils.py +409 -0
  21. runbooks/__init__.py +71 -3
  22. runbooks/__main__.py +13 -0
  23. runbooks/aws/ec2_describe_instances.py +1 -1
  24. runbooks/aws/ec2_run_instances.py +8 -2
  25. runbooks/aws/ec2_start_stop_instances.py +17 -4
  26. runbooks/aws/ec2_unused_volumes.py +5 -1
  27. runbooks/aws/s3_create_bucket.py +4 -2
  28. runbooks/aws/s3_list_objects.py +6 -1
  29. runbooks/aws/tagging_lambda_handler.py +13 -2
  30. runbooks/aws/tags.json +12 -0
  31. runbooks/base.py +353 -0
  32. runbooks/cfat/README.md +49 -0
  33. runbooks/cfat/__init__.py +74 -0
  34. runbooks/cfat/app.ts +644 -0
  35. runbooks/cfat/assessment/__init__.py +40 -0
  36. runbooks/cfat/assessment/asana-import.csv +39 -0
  37. runbooks/cfat/assessment/cfat-checks.csv +31 -0
  38. runbooks/cfat/assessment/cfat.txt +520 -0
  39. runbooks/cfat/assessment/collectors.py +200 -0
  40. runbooks/cfat/assessment/jira-import.csv +39 -0
  41. runbooks/cfat/assessment/runner.py +387 -0
  42. runbooks/cfat/assessment/validators.py +290 -0
  43. runbooks/cfat/cli.py +103 -0
  44. runbooks/cfat/docs/asana-import.csv +24 -0
  45. runbooks/cfat/docs/cfat-checks.csv +31 -0
  46. runbooks/cfat/docs/cfat.txt +335 -0
  47. runbooks/cfat/docs/checks-output.png +0 -0
  48. runbooks/cfat/docs/cloudshell-console-run.png +0 -0
  49. runbooks/cfat/docs/cloudshell-download.png +0 -0
  50. runbooks/cfat/docs/cloudshell-output.png +0 -0
  51. runbooks/cfat/docs/downloadfile.png +0 -0
  52. runbooks/cfat/docs/jira-import.csv +24 -0
  53. runbooks/cfat/docs/open-cloudshell.png +0 -0
  54. runbooks/cfat/docs/report-header.png +0 -0
  55. runbooks/cfat/models.py +1026 -0
  56. runbooks/cfat/package-lock.json +5116 -0
  57. runbooks/cfat/package.json +38 -0
  58. runbooks/cfat/report.py +496 -0
  59. runbooks/cfat/reporting/__init__.py +46 -0
  60. runbooks/cfat/reporting/exporters.py +337 -0
  61. runbooks/cfat/reporting/formatters.py +496 -0
  62. runbooks/cfat/reporting/templates.py +135 -0
  63. runbooks/cfat/run-assessment.sh +23 -0
  64. runbooks/cfat/runner.py +69 -0
  65. runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
  66. runbooks/cfat/src/actions/check-config-existence.ts +37 -0
  67. runbooks/cfat/src/actions/check-control-tower.ts +37 -0
  68. runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
  69. runbooks/cfat/src/actions/check-iam-users.ts +50 -0
  70. runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
  71. runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
  72. runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
  73. runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
  74. runbooks/cfat/src/actions/create-backlog.ts +372 -0
  75. runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
  76. runbooks/cfat/src/actions/create-report.ts +616 -0
  77. runbooks/cfat/src/actions/define-account-type.ts +51 -0
  78. runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
  79. runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
  80. runbooks/cfat/src/actions/get-idc-info.ts +34 -0
  81. runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
  82. runbooks/cfat/src/actions/get-org-details.ts +35 -0
  83. runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
  84. runbooks/cfat/src/actions/get-org-ous.ts +35 -0
  85. runbooks/cfat/src/actions/get-regions.ts +22 -0
  86. runbooks/cfat/src/actions/zip-assessment.ts +27 -0
  87. runbooks/cfat/src/types/index.d.ts +147 -0
  88. runbooks/cfat/tests/__init__.py +141 -0
  89. runbooks/cfat/tests/test_cli.py +340 -0
  90. runbooks/cfat/tests/test_integration.py +290 -0
  91. runbooks/cfat/tests/test_models.py +505 -0
  92. runbooks/cfat/tests/test_reporting.py +354 -0
  93. runbooks/cfat/tsconfig.json +16 -0
  94. runbooks/cfat/webpack.config.cjs +27 -0
  95. runbooks/config.py +260 -0
  96. runbooks/finops/README.md +337 -0
  97. runbooks/finops/__init__.py +86 -0
  98. runbooks/finops/aws_client.py +245 -0
  99. runbooks/finops/cli.py +151 -0
  100. runbooks/finops/cost_processor.py +410 -0
  101. runbooks/finops/dashboard_runner.py +448 -0
  102. runbooks/finops/helpers.py +355 -0
  103. runbooks/finops/main.py +14 -0
  104. runbooks/finops/profile_processor.py +174 -0
  105. runbooks/finops/types.py +66 -0
  106. runbooks/finops/visualisations.py +80 -0
  107. runbooks/inventory/.gitignore +354 -0
  108. runbooks/inventory/ArgumentsClass.py +261 -0
  109. runbooks/inventory/FAILED_SCRIPTS_TROUBLESHOOTING.md +619 -0
  110. runbooks/inventory/Inventory_Modules.py +6130 -0
  111. runbooks/inventory/LandingZone/delete_lz.py +1075 -0
  112. runbooks/inventory/PASSED_SCRIPTS_GUIDE.md +738 -0
  113. runbooks/inventory/README.md +1320 -0
  114. runbooks/inventory/__init__.py +62 -0
  115. runbooks/inventory/account_class.py +532 -0
  116. runbooks/inventory/all_my_instances_wrapper.py +123 -0
  117. runbooks/inventory/aws_decorators.py +201 -0
  118. runbooks/inventory/aws_organization.png +0 -0
  119. runbooks/inventory/cfn_move_stack_instances.py +1526 -0
  120. runbooks/inventory/check_cloudtrail_compliance.py +614 -0
  121. runbooks/inventory/check_controltower_readiness.py +1107 -0
  122. runbooks/inventory/check_landingzone_readiness.py +711 -0
  123. runbooks/inventory/cloudtrail.md +727 -0
  124. runbooks/inventory/collectors/__init__.py +20 -0
  125. runbooks/inventory/collectors/aws_compute.py +518 -0
  126. runbooks/inventory/collectors/aws_networking.py +275 -0
  127. runbooks/inventory/collectors/base.py +222 -0
  128. runbooks/inventory/core/__init__.py +19 -0
  129. runbooks/inventory/core/collector.py +303 -0
  130. runbooks/inventory/core/formatter.py +296 -0
  131. runbooks/inventory/delete_s3_buckets_objects.py +169 -0
  132. runbooks/inventory/discovery.md +81 -0
  133. runbooks/inventory/draw_org_structure.py +748 -0
  134. runbooks/inventory/ec2_vpc_utils.py +341 -0
  135. runbooks/inventory/find_cfn_drift_detection.py +272 -0
  136. runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
  137. runbooks/inventory/find_cfn_stackset_drift.py +733 -0
  138. runbooks/inventory/find_ec2_security_groups.py +669 -0
  139. runbooks/inventory/find_landingzone_versions.py +201 -0
  140. runbooks/inventory/find_vpc_flow_logs.py +1221 -0
  141. runbooks/inventory/inventory.sh +659 -0
  142. runbooks/inventory/list_cfn_stacks.py +558 -0
  143. runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
  144. runbooks/inventory/list_cfn_stackset_operations.py +734 -0
  145. runbooks/inventory/list_cfn_stacksets.py +453 -0
  146. runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
  147. runbooks/inventory/list_ds_directories.py +354 -0
  148. runbooks/inventory/list_ec2_availability_zones.py +286 -0
  149. runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
  150. runbooks/inventory/list_ec2_instances.py +425 -0
  151. runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
  152. runbooks/inventory/list_elbs_load_balancers.py +411 -0
  153. runbooks/inventory/list_enis_network_interfaces.py +526 -0
  154. runbooks/inventory/list_guardduty_detectors.py +568 -0
  155. runbooks/inventory/list_iam_policies.py +404 -0
  156. runbooks/inventory/list_iam_roles.py +518 -0
  157. runbooks/inventory/list_iam_saml_providers.py +359 -0
  158. runbooks/inventory/list_lambda_functions.py +882 -0
  159. runbooks/inventory/list_org_accounts.py +446 -0
  160. runbooks/inventory/list_org_accounts_users.py +354 -0
  161. runbooks/inventory/list_rds_db_instances.py +406 -0
  162. runbooks/inventory/list_route53_hosted_zones.py +318 -0
  163. runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
  164. runbooks/inventory/list_sns_topics.py +360 -0
  165. runbooks/inventory/list_ssm_parameters.py +402 -0
  166. runbooks/inventory/list_vpc_subnets.py +433 -0
  167. runbooks/inventory/list_vpcs.py +422 -0
  168. runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
  169. runbooks/inventory/models/__init__.py +24 -0
  170. runbooks/inventory/models/account.py +192 -0
  171. runbooks/inventory/models/inventory.py +309 -0
  172. runbooks/inventory/models/resource.py +247 -0
  173. runbooks/inventory/recover_cfn_stack_ids.py +205 -0
  174. runbooks/inventory/requirements.txt +12 -0
  175. runbooks/inventory/run_on_multi_accounts.py +211 -0
  176. runbooks/inventory/tests/common_test_data.py +3661 -0
  177. runbooks/inventory/tests/common_test_functions.py +204 -0
  178. runbooks/inventory/tests/setup.py +24 -0
  179. runbooks/inventory/tests/src.py +18 -0
  180. runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
  181. runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
  182. runbooks/inventory/tests/test_inventory_modules.py +55 -0
  183. runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
  184. runbooks/inventory/tests/test_moto_integration_example.py +273 -0
  185. runbooks/inventory/tests/test_org_list_accounts.py +49 -0
  186. runbooks/inventory/update_aws_actions.py +173 -0
  187. runbooks/inventory/update_cfn_stacksets.py +1215 -0
  188. runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
  189. runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
  190. runbooks/inventory/update_s3_public_access_block.py +539 -0
  191. runbooks/inventory/utils/__init__.py +23 -0
  192. runbooks/inventory/utils/aws_helpers.py +510 -0
  193. runbooks/inventory/utils/threading_utils.py +493 -0
  194. runbooks/inventory/utils/validation.py +682 -0
  195. runbooks/inventory/verify_ec2_security_groups.py +1430 -0
  196. runbooks/main.py +1004 -0
  197. runbooks/organizations/__init__.py +12 -0
  198. runbooks/organizations/manager.py +374 -0
  199. runbooks/security/README.md +447 -0
  200. runbooks/security/__init__.py +71 -0
  201. runbooks/{security_baseline → security}/checklist/alternate_contacts.py +8 -1
  202. runbooks/{security_baseline → security}/checklist/bucket_public_access.py +4 -1
  203. runbooks/{security_baseline → security}/checklist/cloudwatch_alarm_configuration.py +9 -2
  204. runbooks/{security_baseline → security}/checklist/guardduty_enabled.py +9 -2
  205. runbooks/{security_baseline → security}/checklist/multi_region_instance_usage.py +5 -1
  206. runbooks/{security_baseline → security}/checklist/root_access_key.py +6 -1
  207. runbooks/{security_baseline → security}/config-origin.json +1 -1
  208. runbooks/{security_baseline → security}/config.json +1 -1
  209. runbooks/{security_baseline → security}/permission.json +1 -1
  210. runbooks/{security_baseline → security}/report_generator.py +10 -2
  211. runbooks/{security_baseline → security}/report_template_en.html +7 -7
  212. runbooks/{security_baseline → security}/report_template_jp.html +7 -7
  213. runbooks/{security_baseline → security}/report_template_kr.html +12 -12
  214. runbooks/{security_baseline → security}/report_template_vn.html +7 -7
  215. runbooks/{security_baseline → security}/run_script.py +8 -2
  216. runbooks/{security_baseline → security}/security_baseline_tester.py +12 -4
  217. runbooks/{security_baseline → security}/utils/common.py +5 -1
  218. runbooks/utils/__init__.py +204 -0
  219. runbooks-0.7.0.dist-info/METADATA +375 -0
  220. runbooks-0.7.0.dist-info/RECORD +249 -0
  221. {runbooks-0.2.5.dist-info → runbooks-0.7.0.dist-info}/WHEEL +1 -1
  222. runbooks-0.7.0.dist-info/entry_points.txt +7 -0
  223. runbooks-0.7.0.dist-info/licenses/LICENSE +201 -0
  224. runbooks-0.7.0.dist-info/top_level.txt +3 -0
  225. runbooks/python101/calculator.py +0 -34
  226. runbooks/python101/config.py +0 -1
  227. runbooks/python101/exceptions.py +0 -16
  228. runbooks/python101/file_manager.py +0 -218
  229. runbooks/python101/toolkit.py +0 -153
  230. runbooks-0.2.5.dist-info/METADATA +0 -439
  231. runbooks-0.2.5.dist-info/RECORD +0 -61
  232. runbooks-0.2.5.dist-info/entry_points.txt +0 -3
  233. runbooks-0.2.5.dist-info/top_level.txt +0 -1
  234. /runbooks/{security_baseline/__init__.py → inventory/tests/script_test_data.py} +0 -0
  235. /runbooks/{security_baseline → security}/checklist/__init__.py +0 -0
  236. /runbooks/{security_baseline → security}/checklist/account_level_bucket_public_access.py +0 -0
  237. /runbooks/{security_baseline → security}/checklist/direct_attached_policy.py +0 -0
  238. /runbooks/{security_baseline → security}/checklist/iam_password_policy.py +0 -0
  239. /runbooks/{security_baseline → security}/checklist/iam_user_mfa.py +0 -0
  240. /runbooks/{security_baseline → security}/checklist/multi_region_trail.py +0 -0
  241. /runbooks/{security_baseline → security}/checklist/root_mfa.py +0 -0
  242. /runbooks/{security_baseline → security}/checklist/root_usage.py +0 -0
  243. /runbooks/{security_baseline → security}/checklist/trail_enabled.py +0 -0
  244. /runbooks/{security_baseline → security}/checklist/trusted_advisor.py +0 -0
  245. /runbooks/{security_baseline → security}/utils/__init__.py +0 -0
  246. /runbooks/{security_baseline → security}/utils/enums.py +0 -0
  247. /runbooks/{security_baseline → security}/utils/language.py +0 -0
  248. /runbooks/{security_baseline → security}/utils/level_const.py +0 -0
  249. /runbooks/{security_baseline → security}/utils/permission_list.py +0 -0
@@ -0,0 +1,40 @@
1
+ import { OrganizationsClient, ListRootsCommand, ListRootsCommandInput, ListRootsResponse } from "@aws-sdk/client-organizations";
2
+ import { PolicyTypesEnabled } from '../types';
3
+
4
+ async function getEnabledOrgPolicyTypes(region: string ): Promise<PolicyTypesEnabled> {
5
+ const orgClient = new OrganizationsClient({ region });
6
+ let policyTypesEnabled:PolicyTypesEnabled = {
7
+ scpEnabled: false,
8
+ tagPolicyEnabled: false,
9
+ backupPolicyEnabled : false
10
+ };
11
+ try {
12
+ const input: ListRootsCommandInput = {};
13
+ const command = new ListRootsCommand(input);
14
+ const roots: ListRootsResponse = await orgClient.send(command);
15
+ if (roots.Roots) {
16
+ if (roots.Roots[0].PolicyTypes) {
17
+ for (const enabledPolicy of roots.Roots[0].PolicyTypes) {
18
+ if (enabledPolicy.Type == 'SERVICE_CONTROL_POLICY' && enabledPolicy.Status == 'ENABLED') {
19
+ policyTypesEnabled.scpEnabled = true;
20
+ }
21
+ if (enabledPolicy.Type == 'TAG_POLICY' && enabledPolicy.Status == 'ENABLED') {
22
+ policyTypesEnabled.tagPolicyEnabled = true;
23
+ }
24
+ if (enabledPolicy.Type == 'BACKUP_POLICY' && enabledPolicy.Status == 'ENABLED') {
25
+ policyTypesEnabled.backupPolicyEnabled = true;
26
+ }
27
+ }// end for
28
+ }// end if
29
+ }// end if
30
+ }
31
+ catch (error) {
32
+ console.error(`An error occurred: ${error}`);
33
+ }
34
+ finally {
35
+ orgClient.destroy();
36
+ return policyTypesEnabled ;
37
+ }
38
+ }
39
+
40
+ export default getEnabledOrgPolicyTypes;
@@ -0,0 +1,26 @@
1
+ import { OrganizationsClient, ListAWSServiceAccessForOrganizationCommand, ListAWSServiceAccessForOrganizationResponse } from "@aws-sdk/client-organizations";
2
+ import { OrgService} from '../types';
3
+
4
+
5
+ async function getEnabledOrgServices(region:string): Promise<OrgService[]> {
6
+ const discoveredOrgServices: OrgService[] = []
7
+ const orgClient = new OrganizationsClient({ region });
8
+ try {
9
+ const orgServiceAccessCommand = new ListAWSServiceAccessForOrganizationCommand({});
10
+ const orgServiceAccessResponse: ListAWSServiceAccessForOrganizationResponse = await orgClient.send(orgServiceAccessCommand);
11
+ if(orgServiceAccessResponse.EnabledServicePrincipals && orgServiceAccessResponse.EnabledServicePrincipals.length > 0){
12
+ orgServiceAccessResponse.EnabledServicePrincipals
13
+ for(const orgService of orgServiceAccessResponse.EnabledServicePrincipals){
14
+ const foundOrgService: OrgService = {service: orgService.ServicePrincipal ?? ""}
15
+ discoveredOrgServices.push(foundOrgService)
16
+ }
17
+ }
18
+ } catch (error) {
19
+ console.error('Error checking service access:', error);
20
+ } finally {
21
+ orgClient.destroy();
22
+ return discoveredOrgServices;
23
+ }
24
+ };
25
+
26
+ export default getEnabledOrgServices
@@ -0,0 +1,34 @@
1
+ import { SSOAdminClient,
2
+ ListInstancesCommand
3
+ } from "@aws-sdk/client-sso-admin";
4
+ import { IdCInfo } from "../types";
5
+
6
+ async function getIdcInfo(regionList: string[]): Promise<IdCInfo> {
7
+ let idcDetails: IdCInfo = {found: false}
8
+ for (const region of regionList){
9
+ const ssoAdminClient = new SSOAdminClient({region});
10
+ try {
11
+ const ssoInput = {
12
+ MaxResults: Number("100")
13
+ };
14
+ const command = new ListInstancesCommand(ssoInput);
15
+ const ssoInstanceResponse = await ssoAdminClient.send(command);
16
+ if (ssoInstanceResponse.Instances && ssoInstanceResponse.Instances.length > 0) {
17
+ const ssoInstance = ssoInstanceResponse.Instances[0];
18
+ idcDetails.found = true;
19
+ idcDetails.region = region;
20
+ idcDetails.arn = ssoInstance.InstanceArn
21
+ idcDetails.id = ssoInstance.IdentityStoreId
22
+ break
23
+ }
24
+ } catch (error) {
25
+ console.log(`Error looking for AWS Identity Center details in region ${region}`)
26
+ }
27
+ finally {
28
+ ssoAdminClient.destroy();
29
+ }
30
+ }
31
+ return idcDetails;
32
+ };
33
+
34
+ export default getIdcInfo;
@@ -0,0 +1,34 @@
1
+ import { OrganizationsClient, ListDelegatedAdministratorsCommand, ListDelegatedServicesForAccountCommand} from "@aws-sdk/client-organizations";
2
+ import { OrgDelegatedAdminAccount } from '../types';
3
+
4
+ async function getOrgDaAccounts(): Promise<OrgDelegatedAdminAccount[]> {
5
+ let orgDaDetails: OrgDelegatedAdminAccount[] = []
6
+ const orgClient = new OrganizationsClient({ region: 'us-east-1' });
7
+ let orgDaDetail: OrgDelegatedAdminAccount = {}
8
+ try {
9
+ const command = new ListDelegatedAdministratorsCommand({});
10
+ const response = await orgClient.send(command);
11
+ if(response.DelegatedAdministrators){
12
+ for (const da of response.DelegatedAdministrators) {
13
+ const input = {AccountId: da.Id};
14
+ const command = new ListDelegatedServicesForAccountCommand(input);
15
+ const accountResponse = await orgClient.send(command);
16
+ if(accountResponse.DelegatedServices){
17
+ orgDaDetail = {
18
+ services: accountResponse.DelegatedServices,
19
+ accountName: da.Name
20
+ }
21
+ orgDaDetails.push(orgDaDetail)
22
+ }
23
+ }
24
+ }
25
+ } catch (error) {
26
+ console.log(`Error looking for delegated services.`)
27
+ }
28
+ finally {
29
+ orgClient.destroy();
30
+ }
31
+ return orgDaDetails;
32
+ };
33
+
34
+ export default getOrgDaAccounts;
@@ -0,0 +1,35 @@
1
+ import { OrganizationsClient, DescribeOrganizationCommand ,ListRootsCommand, DescribeOrganizationResponse, ListRootsResponse} from "@aws-sdk/client-organizations";
2
+ import { OrgInfo } from '../types';
3
+
4
+ async function getOrgDetails(region: string ): Promise<OrgInfo> {
5
+ const orgClient = new OrganizationsClient({ region });
6
+ let orgDetails: OrgInfo = {}
7
+ try {
8
+ const orgDescribeCommand = new DescribeOrganizationCommand({});
9
+ const orgData: DescribeOrganizationResponse = await orgClient.send(orgDescribeCommand)
10
+ if (orgData.Organization) {
11
+ orgDetails.id = orgData.Organization.Id ?? "";
12
+ // console.log(`Organization ID: ${orgDetails.id}` );
13
+ orgDetails.arn = orgData.Organization.Arn ?? "";
14
+ // console.log(`Organization ARN: ${orgDetails.arn}`);
15
+ }
16
+ const command = new ListRootsCommand({})
17
+ const roots: ListRootsResponse = await orgClient.send(command);
18
+ if (roots.Roots) {
19
+ orgDetails.rootOuId = roots.Roots[0].Id
20
+ // console.log(`AWS Org root ou id: ${orgDetails.rootOuId}`)
21
+ }
22
+ else {
23
+ console.log('No info found for your AWS Organization.');
24
+ }
25
+ }
26
+ catch (error) {
27
+ console.error(`An error occurred: ${error}`);
28
+ }
29
+ finally {
30
+ orgClient.destroy();
31
+ return orgDetails ;
32
+ }
33
+ }
34
+
35
+ export default getOrgDetails;
@@ -0,0 +1,44 @@
1
+ import { OrganizationsClient, ListAccountsCommand, ListAccountsCommandOutput } from '@aws-sdk/client-organizations';
2
+ import { OrgMemberAccount } from '../types';
3
+
4
+ async function getOrgMemberAccounts(): Promise<OrgMemberAccount[]> {
5
+ let orgMemberAccountInfo: OrgMemberAccount[] = []
6
+ const orgsClient = new OrganizationsClient({ region: 'us-east-1' });
7
+ const input = {
8
+ MaxResults: Number("200"),
9
+ };
10
+ try {
11
+ let response: ListAccountsCommandOutput = await orgsClient.send(new ListAccountsCommand({}));
12
+ if(response.Accounts && response.Accounts.length > 0){
13
+ for (const account of response.Accounts){
14
+ let orgMemberAccount: OrgMemberAccount = {
15
+ accountName: account.Name,
16
+ accountEmail: account.Email,
17
+ }
18
+ orgMemberAccountInfo.push(orgMemberAccount)
19
+ }
20
+ do {
21
+ if(response.NextToken){
22
+ response = await orgsClient.send(new ListAccountsCommand({NextToken: response.NextToken}));
23
+ if(response.Accounts && response.Accounts.length > 0){
24
+ for (const account of response.Accounts){
25
+ let orgMemberAccount: OrgMemberAccount = {
26
+ accountName: account.Name,
27
+ accountEmail: account.Email,
28
+ }
29
+ orgMemberAccountInfo.push(orgMemberAccount)
30
+ }
31
+ }
32
+ }
33
+ }while(response.NextToken)
34
+ }
35
+ } catch (error) {
36
+ console.error('Error listing AWS accounts:', error);
37
+ }
38
+ finally {
39
+ orgsClient.destroy()
40
+ }
41
+ return orgMemberAccountInfo
42
+ }
43
+
44
+ export default getOrgMemberAccounts;
@@ -0,0 +1,35 @@
1
+ import { OrganizationsClient, ListOrganizationalUnitsForParentCommand, ListOrganizationalUnitsForParentResponse, ListAccountsForParentCommand } from "@aws-sdk/client-organizations";
2
+ import { OUInfo } from '../types';
3
+
4
+
5
+ async function getOrgTopLevelOus(region:string, rootOuId: string ): Promise<OUInfo[]> {
6
+ const orgClient = new OrganizationsClient({ region });
7
+ let topLevelOus: OUInfo[] = []
8
+ try {
9
+ const listOUsCommand = new ListOrganizationalUnitsForParentCommand({
10
+ ParentId: rootOuId,
11
+ });
12
+ const listOUsResponse: ListOrganizationalUnitsForParentResponse = await orgClient.send(listOUsCommand);
13
+ if(listOUsResponse.OrganizationalUnits){
14
+ for (const ou of listOUsResponse.OrganizationalUnits ){
15
+ let topLevelOu:OUInfo = {
16
+ id: ou.Id,
17
+ name: ou.Name
18
+ }
19
+ const accountResponse = await orgClient.send(new ListAccountsForParentCommand({ ParentId: ou.Id }));
20
+ if(accountResponse.Accounts && accountResponse.Accounts.length > 0){
21
+ topLevelOu.accounts = accountResponse.Accounts
22
+ }
23
+ topLevelOus.push(topLevelOu)
24
+ }
25
+ }
26
+ } catch (error) {
27
+ console.error('Error checking service access:', error);
28
+ return []
29
+ } finally {
30
+ orgClient.destroy();
31
+ }
32
+ return topLevelOus
33
+ };
34
+
35
+ export default getOrgTopLevelOus
@@ -0,0 +1,22 @@
1
+ import { EC2Client, DescribeRegionsCommand, DescribeRegionsResult } from "@aws-sdk/client-ec2";
2
+
3
+ async function getAllRegions(): Promise<string[]> {
4
+ // grabbing all regions from us-east-1
5
+ const ec2Client = new EC2Client({ region: "us-east-1" });
6
+ try {
7
+ const describeRegionsCommand = new DescribeRegionsCommand({});
8
+ const response = await ec2Client.send(describeRegionsCommand);
9
+ const regions: string[] = [];
10
+ for (const region of response.Regions || []) {
11
+ regions.push(region.RegionName || "");
12
+ }
13
+ return regions
14
+ } catch (error) {
15
+ console.error("Error retrieving regions:", error);
16
+ return []
17
+ } finally {
18
+ ec2Client.destroy();
19
+ }
20
+ }
21
+
22
+ export default getAllRegions
@@ -0,0 +1,27 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import archiver from 'archiver';
4
+
5
+ async function zipAssessmentFiles(): Promise<void> {
6
+ try {
7
+ const output = fs.createWriteStream(path.join(process.cwd(), 'assessment.zip'));
8
+ const archive = archiver('zip', {
9
+ zlib: { level: 9 }, // Sets the compression level.
10
+ });
11
+
12
+ archive.pipe(output);
13
+
14
+ // Add the files to the archive
15
+ archive.file(path.join(process.cwd(), 'cfat.txt'), { name: 'cfat.txt' });
16
+ archive.file(path.join(process.cwd(), 'cfat-checks.csv'), { name: 'cfat-checks.csv' });
17
+ archive.file(path.join(process.cwd(), 'asana-import.csv'), { name: 'asana-import.csv' });
18
+ archive.file(path.join(process.cwd(), 'jira-import.csv'), { name: 'jira-import.csv' });
19
+ archive.finalize();
20
+
21
+ console.log('Zip file created successfully!');
22
+ } catch (err) {
23
+ console.error('Error creating zip file:', err);
24
+ }
25
+ }
26
+
27
+ export default zipAssessmentFiles;
@@ -0,0 +1,147 @@
1
+ import { DelegatedServices } from "@aws-sdk/client-organizations";
2
+
3
+ export interface CfatCheck {
4
+ check:string;
5
+ description:string;
6
+ status:string;
7
+ required:boolean;
8
+ weight:number
9
+ loe:number
10
+ remediationLink?: string;
11
+ }
12
+
13
+ export interface AccountType {
14
+ isInOrganization: boolean;
15
+ isManagementAccount?: boolean;
16
+ };
17
+
18
+ export interface ControlTowerInfo {
19
+ controlTowerRegion?: string;
20
+ latestAvailableVersion?: string;
21
+ deployedVersion?: string
22
+ driftStatus?: string;
23
+ status?: string
24
+ }
25
+
26
+ export interface Ec2Check {
27
+ region?: string;
28
+ ec2Found?: boolean
29
+ }
30
+
31
+ export interface CloudTrailInfo{
32
+ region?: string;
33
+ trailFound?: boolean;
34
+ isOrgTrail?: boolean;
35
+ isMultiRegion?: boolean;
36
+ }
37
+ export interface VpcCheck {
38
+ region?: string;
39
+ vpcFound?: boolean
40
+ }
41
+ export interface IamUserInfo {
42
+ userName: string;
43
+ accessKeyId?: string;
44
+ lastUsed?: string;
45
+ };
46
+
47
+ export interface IdCInfo{
48
+ found: boolean;
49
+ region?: string
50
+ arn?: string;
51
+ id?: string;
52
+ }
53
+
54
+ export interface LegacyCurInfo{
55
+ isLegacyCurSetup: boolean;
56
+ };
57
+
58
+ export interface OrgService {
59
+ service: string;
60
+ };
61
+
62
+ export interface OrgInfo {
63
+ arn?: string,
64
+ id?: string,
65
+ rootOuId?: string
66
+ }
67
+
68
+ interface OUInfo {
69
+ id?: string;
70
+ name?: string;
71
+ accounts?: Account[];
72
+ }
73
+
74
+ export interface OrgCloudFormationStatus {
75
+ status: string
76
+ };
77
+
78
+ export interface PolicyTypesEnabled {
79
+ scpEnabled: boolean;
80
+ tagPolicyEnabled: boolean;
81
+ backupPolicyEnabled: boolean;
82
+ };
83
+
84
+ export interface OrgDelegatedAdminAccount {
85
+ accountName?: string;
86
+ services?: DelegatedServices[];
87
+ }
88
+
89
+ export interface ConfigInfo {
90
+ region?: string;
91
+ configRecorderFound?: boolean
92
+ configDeliveryChannelFound?: boolean
93
+ }
94
+
95
+ export interface OrgMemberAccount {
96
+ accountName?: string;
97
+ accountEmail?: string;
98
+ }
99
+
100
+ export interface Task {
101
+ title: string;
102
+ category?: string;
103
+ detail?:string;
104
+ remediationLink?: string;
105
+ }
106
+
107
+ export interface CloudFoundationAssessment {
108
+ organizationDeploy?: Boolean;
109
+ managementAccount?: Boolean;
110
+ isLegacyCurSetup?: Boolean;
111
+ vpcChecks?: VpcCheck[];
112
+ ec2Checks?: Ec2Check[];
113
+ iamUserChecks?: IamUserInfo[];
114
+ orgArn?: string,
115
+ orgId?: string,
116
+ orgRootOuId?: string;
117
+ orgServices?: OrgService[]
118
+ orgCloudFormationStatus?: string;
119
+ orgMemberAccounts?: OrgMemberAccount[];
120
+ orgDelegatedAdminAccounts?: OrgDelegatedAdminAccount[];
121
+ orgOuInfo?: OUInfo[];
122
+ controlTowerRegion?: string;
123
+ controlTowerLatestAvailableVersion?: string;
124
+ controlTowerDeployedVersion?: string
125
+ controlTowerDriftStatus?: string;
126
+ controlTowerStatus?: string;
127
+ scpEnabled?: boolean;
128
+ tagPolicyEnabled?: boolean;
129
+ backupPolicyEnabled?: boolean;
130
+ configDetails?: ConfigInfo[];
131
+ cloudTrailDetails?: CloudTrailInfo[];
132
+ idcInfo?: IdCInfo;
133
+ cfatChecks?: CfatCheck[];
134
+ }
135
+
136
+ interface JiraCSVData {
137
+ [key: string]: {
138
+ Summary: string;
139
+ Description: string;
140
+ Status: string;
141
+ };
142
+ }
143
+
144
+ interface CSVOptions {
145
+ headers?: { [key: string]: string };
146
+ filename?: string;
147
+ }
@@ -0,0 +1,141 @@
1
+ """
2
+ Comprehensive Test Suite for Cloud Foundations Assessment Tool (CFAT).
3
+
4
+ This test module provides enterprise-grade testing for CFAT functionality
5
+ including:
6
+
7
+ - Unit tests for all core components
8
+ - Integration tests with moto AWS mocking
9
+ - CLI argument parsing validation
10
+ - Performance and load testing
11
+ - Compliance framework testing
12
+ - Report generation testing
13
+
14
+ Testing Strategy:
15
+ - Use moto for AWS service mocking to avoid real AWS calls
16
+ - Test argument parsing separately from AWS API calls
17
+ - Create common test fixtures to reduce duplication (DRY)
18
+ - Focus on integration tests that verify real-world usage patterns
19
+ - Maintain test compatibility with existing workflows
20
+
21
+ The test suite is designed to be autonomous and can run without
22
+ AWS credentials or network access.
23
+ """
24
+
25
+ from datetime import datetime
26
+ from typing import Any, Dict
27
+
28
+ import pytest
29
+
30
+ from runbooks.cfat.models import AssessmentReport, AssessmentResult, AssessmentSummary, CheckStatus, Severity
31
+
32
+
33
+ # Test fixtures and utilities
34
+ def create_sample_assessment_result(
35
+ finding_id: str = "TEST-001",
36
+ check_name: str = "test_check",
37
+ category: str = "test",
38
+ status: CheckStatus = CheckStatus.PASS,
39
+ severity: Severity = Severity.INFO,
40
+ message: str = "Test check passed",
41
+ execution_time: float = 0.1,
42
+ ) -> AssessmentResult:
43
+ """Create a sample assessment result for testing."""
44
+ return AssessmentResult(
45
+ finding_id=finding_id,
46
+ check_name=check_name,
47
+ check_category=category,
48
+ status=status,
49
+ severity=severity,
50
+ message=message,
51
+ execution_time=execution_time,
52
+ recommendations=["Test recommendation"] if status == CheckStatus.FAIL else [],
53
+ )
54
+
55
+
56
+ def create_sample_assessment_summary(
57
+ total_checks: int = 10,
58
+ passed_checks: int = 8,
59
+ failed_checks: int = 2,
60
+ skipped_checks: int = 0,
61
+ error_checks: int = 0,
62
+ warnings: int = 1,
63
+ critical_issues: int = 1,
64
+ total_execution_time: float = 5.0,
65
+ ) -> AssessmentSummary:
66
+ """Create a sample assessment summary for testing."""
67
+ return AssessmentSummary(
68
+ total_checks=total_checks,
69
+ passed_checks=passed_checks,
70
+ failed_checks=failed_checks,
71
+ skipped_checks=skipped_checks,
72
+ error_checks=error_checks,
73
+ warnings=warnings,
74
+ critical_issues=critical_issues,
75
+ total_execution_time=total_execution_time,
76
+ )
77
+
78
+
79
+ def create_sample_assessment_report(
80
+ account_id: str = "123456789012",
81
+ region: str = "us-east-1",
82
+ profile: str = "test",
83
+ version: str = "0.5.0",
84
+ num_results: int = 5,
85
+ ) -> AssessmentReport:
86
+ """Create a sample assessment report for testing."""
87
+ results = []
88
+ for i in range(num_results):
89
+ status = CheckStatus.PASS if i % 3 != 0 else CheckStatus.FAIL
90
+ severity = Severity.CRITICAL if i == 0 else Severity.WARNING if i == 1 else Severity.INFO
91
+ result = create_sample_assessment_result(
92
+ finding_id=f"TEST-{i + 1:03d}",
93
+ check_name=f"test_check_{i + 1}",
94
+ category="test",
95
+ status=status,
96
+ severity=severity,
97
+ message=f"Test check {i + 1} result",
98
+ )
99
+ results.append(result)
100
+
101
+ # Calculate summary from results
102
+ passed = len([r for r in results if r.status == CheckStatus.PASS])
103
+ failed = len([r for r in results if r.status == CheckStatus.FAIL])
104
+ warnings = len([r for r in results if r.severity == Severity.WARNING])
105
+ critical = len([r for r in results if r.severity == Severity.CRITICAL])
106
+
107
+ summary = create_sample_assessment_summary(
108
+ total_checks=len(results),
109
+ passed_checks=passed,
110
+ failed_checks=failed,
111
+ warnings=warnings,
112
+ critical_issues=critical,
113
+ )
114
+
115
+ return AssessmentReport(
116
+ account_id=account_id,
117
+ region=region,
118
+ profile=profile,
119
+ version=version,
120
+ included_checks=["test"],
121
+ severity_threshold=Severity.INFO,
122
+ results=results,
123
+ summary=summary,
124
+ )
125
+
126
+
127
+ # Common test constants
128
+ TEST_ACCOUNT_ID = "123456789012"
129
+ TEST_REGION = "us-east-1"
130
+ TEST_PROFILE = "test-profile"
131
+
132
+ # Test markers
133
+ pytest_markers = [
134
+ "unit: Unit tests (fast, no external dependencies)",
135
+ "integration: Integration tests with moto AWS mocking",
136
+ "cli: Tests CLI argument parsing and validation",
137
+ "assessment: Tests for assessment engine functionality",
138
+ "reporting: Tests for report generation",
139
+ "models: Tests for data model validation",
140
+ "slow: Slow tests (long-running operations)",
141
+ ]