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.
- conftest.py +26 -0
- jupyter-agent/.env.template +2 -0
- jupyter-agent/.gitattributes +35 -0
- jupyter-agent/README.md +16 -0
- jupyter-agent/app.py +256 -0
- jupyter-agent/cloudops-agent.png +0 -0
- jupyter-agent/ds-system-prompt.txt +154 -0
- jupyter-agent/jupyter-agent.png +0 -0
- jupyter-agent/llama3_template.jinja +123 -0
- jupyter-agent/requirements.txt +9 -0
- jupyter-agent/utils.py +409 -0
- runbooks/__init__.py +71 -3
- runbooks/__main__.py +13 -0
- runbooks/aws/ec2_describe_instances.py +1 -1
- runbooks/aws/ec2_run_instances.py +8 -2
- runbooks/aws/ec2_start_stop_instances.py +17 -4
- runbooks/aws/ec2_unused_volumes.py +5 -1
- runbooks/aws/s3_create_bucket.py +4 -2
- runbooks/aws/s3_list_objects.py +6 -1
- runbooks/aws/tagging_lambda_handler.py +13 -2
- runbooks/aws/tags.json +12 -0
- runbooks/base.py +353 -0
- runbooks/cfat/README.md +49 -0
- runbooks/cfat/__init__.py +74 -0
- runbooks/cfat/app.ts +644 -0
- runbooks/cfat/assessment/__init__.py +40 -0
- runbooks/cfat/assessment/asana-import.csv +39 -0
- runbooks/cfat/assessment/cfat-checks.csv +31 -0
- runbooks/cfat/assessment/cfat.txt +520 -0
- runbooks/cfat/assessment/collectors.py +200 -0
- runbooks/cfat/assessment/jira-import.csv +39 -0
- runbooks/cfat/assessment/runner.py +387 -0
- runbooks/cfat/assessment/validators.py +290 -0
- runbooks/cfat/cli.py +103 -0
- runbooks/cfat/docs/asana-import.csv +24 -0
- runbooks/cfat/docs/cfat-checks.csv +31 -0
- runbooks/cfat/docs/cfat.txt +335 -0
- runbooks/cfat/docs/checks-output.png +0 -0
- runbooks/cfat/docs/cloudshell-console-run.png +0 -0
- runbooks/cfat/docs/cloudshell-download.png +0 -0
- runbooks/cfat/docs/cloudshell-output.png +0 -0
- runbooks/cfat/docs/downloadfile.png +0 -0
- runbooks/cfat/docs/jira-import.csv +24 -0
- runbooks/cfat/docs/open-cloudshell.png +0 -0
- runbooks/cfat/docs/report-header.png +0 -0
- runbooks/cfat/models.py +1026 -0
- runbooks/cfat/package-lock.json +5116 -0
- runbooks/cfat/package.json +38 -0
- runbooks/cfat/report.py +496 -0
- runbooks/cfat/reporting/__init__.py +46 -0
- runbooks/cfat/reporting/exporters.py +337 -0
- runbooks/cfat/reporting/formatters.py +496 -0
- runbooks/cfat/reporting/templates.py +135 -0
- runbooks/cfat/run-assessment.sh +23 -0
- runbooks/cfat/runner.py +69 -0
- runbooks/cfat/src/actions/check-cloudtrail-existence.ts +43 -0
- runbooks/cfat/src/actions/check-config-existence.ts +37 -0
- runbooks/cfat/src/actions/check-control-tower.ts +37 -0
- runbooks/cfat/src/actions/check-ec2-existence.ts +46 -0
- runbooks/cfat/src/actions/check-iam-users.ts +50 -0
- runbooks/cfat/src/actions/check-legacy-cur.ts +30 -0
- runbooks/cfat/src/actions/check-org-cloudformation.ts +30 -0
- runbooks/cfat/src/actions/check-vpc-existence.ts +43 -0
- runbooks/cfat/src/actions/create-asanaimport.ts +14 -0
- runbooks/cfat/src/actions/create-backlog.ts +372 -0
- runbooks/cfat/src/actions/create-jiraimport.ts +15 -0
- runbooks/cfat/src/actions/create-report.ts +616 -0
- runbooks/cfat/src/actions/define-account-type.ts +51 -0
- runbooks/cfat/src/actions/get-enabled-org-policy-types.ts +40 -0
- runbooks/cfat/src/actions/get-enabled-org-services.ts +26 -0
- runbooks/cfat/src/actions/get-idc-info.ts +34 -0
- runbooks/cfat/src/actions/get-org-da-accounts.ts +34 -0
- runbooks/cfat/src/actions/get-org-details.ts +35 -0
- runbooks/cfat/src/actions/get-org-member-accounts.ts +44 -0
- runbooks/cfat/src/actions/get-org-ous.ts +35 -0
- runbooks/cfat/src/actions/get-regions.ts +22 -0
- runbooks/cfat/src/actions/zip-assessment.ts +27 -0
- runbooks/cfat/src/types/index.d.ts +147 -0
- runbooks/cfat/tests/__init__.py +141 -0
- runbooks/cfat/tests/test_cli.py +340 -0
- runbooks/cfat/tests/test_integration.py +290 -0
- runbooks/cfat/tests/test_models.py +505 -0
- runbooks/cfat/tests/test_reporting.py +354 -0
- runbooks/cfat/tsconfig.json +16 -0
- runbooks/cfat/webpack.config.cjs +27 -0
- runbooks/config.py +260 -0
- runbooks/finops/__init__.py +88 -0
- runbooks/finops/aws_client.py +245 -0
- runbooks/finops/cli.py +151 -0
- runbooks/finops/cost_processor.py +410 -0
- runbooks/finops/dashboard_runner.py +448 -0
- runbooks/finops/helpers.py +355 -0
- runbooks/finops/main.py +14 -0
- runbooks/finops/profile_processor.py +174 -0
- runbooks/finops/types.py +66 -0
- runbooks/finops/visualisations.py +80 -0
- runbooks/inventory/.gitignore +354 -0
- runbooks/inventory/ArgumentsClass.py +261 -0
- runbooks/inventory/Inventory_Modules.py +6130 -0
- runbooks/inventory/LandingZone/delete_lz.py +1075 -0
- runbooks/inventory/README.md +1320 -0
- runbooks/inventory/__init__.py +62 -0
- runbooks/inventory/account_class.py +532 -0
- runbooks/inventory/all_my_instances_wrapper.py +123 -0
- runbooks/inventory/aws_decorators.py +201 -0
- runbooks/inventory/cfn_move_stack_instances.py +1526 -0
- runbooks/inventory/check_cloudtrail_compliance.py +614 -0
- runbooks/inventory/check_controltower_readiness.py +1107 -0
- runbooks/inventory/check_landingzone_readiness.py +711 -0
- runbooks/inventory/cloudtrail.md +727 -0
- runbooks/inventory/collectors/__init__.py +20 -0
- runbooks/inventory/collectors/aws_compute.py +518 -0
- runbooks/inventory/collectors/aws_networking.py +275 -0
- runbooks/inventory/collectors/base.py +222 -0
- runbooks/inventory/core/__init__.py +19 -0
- runbooks/inventory/core/collector.py +303 -0
- runbooks/inventory/core/formatter.py +296 -0
- runbooks/inventory/delete_s3_buckets_objects.py +169 -0
- runbooks/inventory/discovery.md +81 -0
- runbooks/inventory/draw_org_structure.py +748 -0
- runbooks/inventory/ec2_vpc_utils.py +341 -0
- runbooks/inventory/find_cfn_drift_detection.py +272 -0
- runbooks/inventory/find_cfn_orphaned_stacks.py +719 -0
- runbooks/inventory/find_cfn_stackset_drift.py +733 -0
- runbooks/inventory/find_ec2_security_groups.py +669 -0
- runbooks/inventory/find_landingzone_versions.py +201 -0
- runbooks/inventory/find_vpc_flow_logs.py +1221 -0
- runbooks/inventory/inventory.sh +659 -0
- runbooks/inventory/list_cfn_stacks.py +558 -0
- runbooks/inventory/list_cfn_stackset_operation_results.py +252 -0
- runbooks/inventory/list_cfn_stackset_operations.py +734 -0
- runbooks/inventory/list_cfn_stacksets.py +453 -0
- runbooks/inventory/list_config_recorders_delivery_channels.py +681 -0
- runbooks/inventory/list_ds_directories.py +354 -0
- runbooks/inventory/list_ec2_availability_zones.py +286 -0
- runbooks/inventory/list_ec2_ebs_volumes.py +244 -0
- runbooks/inventory/list_ec2_instances.py +425 -0
- runbooks/inventory/list_ecs_clusters_and_tasks.py +562 -0
- runbooks/inventory/list_elbs_load_balancers.py +411 -0
- runbooks/inventory/list_enis_network_interfaces.py +526 -0
- runbooks/inventory/list_guardduty_detectors.py +568 -0
- runbooks/inventory/list_iam_policies.py +404 -0
- runbooks/inventory/list_iam_roles.py +518 -0
- runbooks/inventory/list_iam_saml_providers.py +359 -0
- runbooks/inventory/list_lambda_functions.py +882 -0
- runbooks/inventory/list_org_accounts.py +446 -0
- runbooks/inventory/list_org_accounts_users.py +354 -0
- runbooks/inventory/list_rds_db_instances.py +406 -0
- runbooks/inventory/list_route53_hosted_zones.py +318 -0
- runbooks/inventory/list_servicecatalog_provisioned_products.py +575 -0
- runbooks/inventory/list_sns_topics.py +360 -0
- runbooks/inventory/list_ssm_parameters.py +402 -0
- runbooks/inventory/list_vpc_subnets.py +433 -0
- runbooks/inventory/list_vpcs.py +422 -0
- runbooks/inventory/lockdown_cfn_stackset_role.py +224 -0
- runbooks/inventory/models/__init__.py +24 -0
- runbooks/inventory/models/account.py +192 -0
- runbooks/inventory/models/inventory.py +309 -0
- runbooks/inventory/models/resource.py +247 -0
- runbooks/inventory/recover_cfn_stack_ids.py +205 -0
- runbooks/inventory/requirements.txt +12 -0
- runbooks/inventory/run_on_multi_accounts.py +211 -0
- runbooks/inventory/tests/common_test_data.py +3661 -0
- runbooks/inventory/tests/common_test_functions.py +204 -0
- runbooks/inventory/tests/script_test_data.py +0 -0
- runbooks/inventory/tests/setup.py +24 -0
- runbooks/inventory/tests/src.py +18 -0
- runbooks/inventory/tests/test_cfn_describe_stacks.py +208 -0
- runbooks/inventory/tests/test_ec2_describe_instances.py +162 -0
- runbooks/inventory/tests/test_inventory_modules.py +55 -0
- runbooks/inventory/tests/test_lambda_list_functions.py +86 -0
- runbooks/inventory/tests/test_moto_integration_example.py +273 -0
- runbooks/inventory/tests/test_org_list_accounts.py +49 -0
- runbooks/inventory/update_aws_actions.py +173 -0
- runbooks/inventory/update_cfn_stacksets.py +1215 -0
- runbooks/inventory/update_cloudwatch_logs_retention_policy.py +294 -0
- runbooks/inventory/update_iam_roles_cross_accounts.py +478 -0
- runbooks/inventory/update_s3_public_access_block.py +539 -0
- runbooks/inventory/utils/__init__.py +23 -0
- runbooks/inventory/utils/aws_helpers.py +510 -0
- runbooks/inventory/utils/threading_utils.py +493 -0
- runbooks/inventory/utils/validation.py +682 -0
- runbooks/inventory/verify_ec2_security_groups.py +1430 -0
- runbooks/main.py +785 -0
- runbooks/organizations/__init__.py +12 -0
- runbooks/organizations/manager.py +374 -0
- runbooks/security_baseline/README.md +324 -0
- runbooks/security_baseline/checklist/alternate_contacts.py +8 -1
- runbooks/security_baseline/checklist/bucket_public_access.py +4 -1
- runbooks/security_baseline/checklist/cloudwatch_alarm_configuration.py +9 -2
- runbooks/security_baseline/checklist/guardduty_enabled.py +9 -2
- runbooks/security_baseline/checklist/multi_region_instance_usage.py +5 -1
- runbooks/security_baseline/checklist/root_access_key.py +6 -1
- runbooks/security_baseline/config-origin.json +1 -1
- runbooks/security_baseline/config.json +1 -1
- runbooks/security_baseline/permission.json +1 -1
- runbooks/security_baseline/report_generator.py +10 -2
- runbooks/security_baseline/report_template_en.html +8 -8
- runbooks/security_baseline/report_template_jp.html +8 -8
- runbooks/security_baseline/report_template_kr.html +13 -13
- runbooks/security_baseline/report_template_vn.html +8 -8
- runbooks/security_baseline/requirements.txt +7 -0
- runbooks/security_baseline/run_script.py +8 -2
- runbooks/security_baseline/security_baseline_tester.py +10 -2
- runbooks/security_baseline/utils/common.py +5 -1
- runbooks/utils/__init__.py +204 -0
- runbooks-0.6.1.dist-info/METADATA +373 -0
- runbooks-0.6.1.dist-info/RECORD +237 -0
- {runbooks-0.2.3.dist-info → runbooks-0.6.1.dist-info}/WHEEL +1 -1
- runbooks-0.6.1.dist-info/entry_points.txt +7 -0
- runbooks-0.6.1.dist-info/licenses/LICENSE +201 -0
- runbooks-0.6.1.dist-info/top_level.txt +3 -0
- runbooks/python101/calculator.py +0 -34
- runbooks/python101/config.py +0 -1
- runbooks/python101/exceptions.py +0 -16
- runbooks/python101/file_manager.py +0 -218
- runbooks/python101/toolkit.py +0 -153
- runbooks-0.2.3.dist-info/METADATA +0 -435
- runbooks-0.2.3.dist-info/RECORD +0 -61
- runbooks-0.2.3.dist-info/entry_points.txt +0 -3
- runbooks-0.2.3.dist-info/top_level.txt +0 -1
@@ -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
|
+
]
|