diff --git a/util/multi-account/Audit_Exec_Role.yaml b/util/multi-account/Audit_Exec_Role.yaml new file mode 100644 index 00000000..673defaa --- /dev/null +++ b/util/multi-account/Audit_Exec_Role.yaml @@ -0,0 +1,73 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' +Description: Prowler Auditing Role - in Control Tower pick AWSControlTowerStackSetRole for IAM role and AWSControlTowerExecution for execution + +Parameters: + + AuditorAccountId: + Default: 987600001234 + Description: AWS Account ID where the audit tooling executes + Type: Number + AuditRolePathName: + Default: '/audit/prowler/XA_AuditRole_Prowler' + Description: Path for role name in audit tooling account + Type: String + +Resources: + XAAuditRole: + Type: "AWS::IAM::Role" + Properties: # /audit/prowler/XA_AuditRole_Prowler + RoleName: XA_AuditRole_Prowler + Path: "/audit/prowler/" + ManagedPolicyArns: + - arn:aws:iam::aws:policy/SecurityAudit + - arn:aws:iam::aws:policy/AWSOrganizationsReadOnlyAccess + - arn:aws:iam::aws:policy/IAMReadOnlyAccess + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + AWS: # TODO: review permissions to see if this can be narrowed down - code build only perhaps + - !Sub "arn:aws:iam::${AuditorAccountId}:root" + Action: + - "sts:AssumeRole" + - Effect: "Allow" + Principal: + Service: + - "codebuild.amazonaws.com" + Action: + - "sts:AssumeRole" + # TODO: restrict to only AuditorAccount only + Policies: + - PolicyName: "ProwlerPolicyAdditions" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: "ProwlerPolicyAdditions" + Effect: "Allow" + Resource: "*" + Action: + - "acm:describecertificate" + - "acm:listcertificates" + - "es:describeelasticsearchdomainconfig" + - "logs:DescribeLogGroups" + - "logs:DescribeMetricFilters" + - "ses:getidentityverificationattributes" + - "sns:listsubscriptionsbytopic" + - "guardduty:ListDetectors" + - "guardduty:GetDetector" + - "S3:GetEncryptionConfiguration" + - "trustedadvisor:Describe*" + - "cloudtrail:GetEventSelectors" + - "apigateway:GET" + - "support:*" + Metadata: + cfn_nag: + rules_to_suppress: + - id: W28 + reason: "the role name is intentionally static" + - id: W11 + reason: "the policy grants read/view/audit access only, to all resources, by design" + - id: F3 + reason: "Support does not allow or deny access to individual actions" diff --git a/util/multi-account/Audit_Pipeline.yaml b/util/multi-account/Audit_Pipeline.yaml new file mode 100644 index 00000000..c8b7ef8c --- /dev/null +++ b/util/multi-account/Audit_Pipeline.yaml @@ -0,0 +1,414 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' +Description: Prowler Auditing Tools Stack + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Organizations and Accounts" + Parameters: + - pOrgMasterAccounts + - pOrgExcludedAccounts + - pStandAloneAccounts + - Label: + default: "Check Group and Execution" + Parameters: + - pProwlerCheckGroup + - pAuditEveryXHours + - Label: + default: "Advanced" + Parameters: + - pTimeoutMinutes + - pAuditRolePathName + - pCustomProwlerRepo + - pCustomProwlerCloneArgs + ParameterLabels: + pOrgMasterAccounts: + default: "Organization Master Accounts" + pOrgExcludedAccounts: + default: "Excluded Organiztion Members" + pStandAloneAccounts: + default: "Stand-alone Accounts" + pProwlerCheckGroup: + default: "Prowler Check Group" + pAuditEveryXHours: + default: "Perform Audit every X hours" + pTimeoutMinutes: + default: "Permit Audit to run for X minutes" + pAuditRolePathName: + default: "Custom audit role path" + pCustomProwlerRepo: + default: "Custom git repo location for prowler" + pCustomProwlerCloneArgs: + default: "Custom arguments to git clone --depth 1" + +Parameters: + pAuditEveryXHours: + Default: 24 + Type: Number + Description: Number of hours between prowler audit runs. + MinValue: 2 + MaxValue: 168 + pTimeoutMinutes: + Default: 30 + Type: Number + Description: Timeout for running prowler across the fleet + MinValue: 5 + MaxValue: 480 + pAuditRolePathName: + Default: '/audit/prowler/XA_AuditRole_Prowler' + Type: String + Description: Role path and name which prowler will assume in the target accounts (Audit_Exec_Role.yaml) + # TODO: Validation: begins with "/" and does NOT end with "/" + pOrgMasterAccounts: + Description: Comma Separated list of Organization Master Accounts, or 'none' + Default: 'none' + Type: String + MinLength: 4 + AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$ + ConstraintDescription: comma separated list 12-digit account numbers, or 'none' + pOrgExcludedAccounts: # Comma Separated list of Org Member Accounts to EXCLUDE + Description: Comma Separated list of Skipped Organization Member Accounts, or 'none' + Default: 'none' + Type: String + MinLength: 4 + AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$ + ConstraintDescription: comma separated list 12-digit account numbers, or 'none' + pStandAloneAccounts: # Comma Separated list of Stand-Alone Accounts + Description: Comma Separated list of Stand-alone Accounts, or 'none' + Default: 'none' + Type: String + MinLength: 4 + AllowedPattern: ^(none|([0-9]{12}(,[0-9]{12})*))$ + ConstraintDescription: comma separated list 12-digit account numbers, or 'none' + pProwlerCheckGroup: + Default: 'cislevel1' + Type: String + Description: Which group of checks should prowler run + AllowedValues: + - 'group1' + - 'group2' + - 'group3' + - 'group4' + - 'cislevel1' + - 'cislevel2' + - 'extras' + - 'forensics-ready' + - 'gdpr' + - 'hipaa' + - 'secrets' + - 'apigateway' + - 'rds' + pCustomProwlerRepo: + Type: String + Default: 'https://github.com/toniblyx/prowler.git' + MinLength: 10 + pCustomProwlerCloneArgs: + Type: String + Default: '--branch master' + MinLength: 0 + ##### TODO + # pResultsBucket: # if specified, use an existing bucket for the data + # pEnableAthena: + # Default: false + # Type: Boolean + # Description: Set to true to enable creation of Athena/QuickSight resources + +#### TODO +# Conditions: +# cUseAthena: False + +Resources: + + # S3 Bucket for Results, Config + ProwlerResults: + Type: "AWS::S3::Bucket" + Properties: + # BucketName: !Sub "audit-results-${AWS::AccountId}" + Tags: + - Key: "data-type" + Value: "it-audit:sensitive" + - Key: "data-public" + Value: "NO" + AccessControl: Private + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + PublicAccessBlockConfiguration: + BlockPublicAcls: True + BlockPublicPolicy: True + IgnorePublicAcls: True + RestrictPublicBuckets: True + # LoggingConfiguration: + # TODO: Enable BucketLogging - requires more parameters + DeletionPolicy: "Retain" + Metadata: + cfn_nag: + rules_to_suppress: + - id: W35 + reason: "Bucket logging requires additional configuration not yet supported by this template" + + # Policy to allow assuming the XA_AuditRole_Prowler in target accounts + ProwlerAuditManagerRole: + Type: AWS::IAM::Role + Properties: + RoleName: AuditManagerRole_Prowler + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: AssumeRole-XA_AuditRole_Prowler + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - sts:AssumeRole + Resource: + - !Sub "arn:aws:iam::*:role${pAuditRolePathName}" + - Effect: Allow + Action: + - s3:PutObject + - s3:GetObject + - s3:GetObjectVersion + Resource: + - !Sub "${ProwlerResults.Arn}/*" + - Effect: Allow + Action: + - s3:ListBucket + - s3:HeadBucket + - s3:GetBucketLocation + - s3:GetBucketAcl + Resource: + - !Sub "${ProwlerResults.Arn}" + - Effect: Allow + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + Resource: + - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*" + - !Sub "${ProwlerResults.Arn}" + - Effect: Allow + Action: + - ssm:GetParameters + Resource: + - !Sub "arn:aws:ssm:us-east-1:${AWS::AccountId}:parameter/audit/prowler/config/*" + Metadata: + cfn_nag: + rules_to_suppress: + - id: W28 + reason: "the role name is intentionally static" + - id: W11 + reason: "not sure where the violation of w11 is" + + ## Code Build Job + ProwlerBuildProject: + Type: "AWS::CodeBuild::Project" + Properties: + Name: PerformProwlerAudit + Description: "Run Prowler audit on accounts in targeted organizations" + QueuedTimeoutInMinutes: 480 + TimeoutInMinutes: !Ref pTimeoutMinutes + ServiceRole: !Ref ProwlerAuditManagerRole + EncryptionKey: !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:alias/aws/s3" + Environment: + Type: "LINUX_CONTAINER" + ComputeType: "BUILD_GENERAL1_MEDIUM" + PrivilegedMode: False + Image: "aws/codebuild/standard:2.0-1.12.0" + ImagePullCredentialsType: "CODEBUILD" + Artifacts: # s3://stack-prowlerresults-randomness/prowler/results/... + Name: "results" + Type: "S3" + Location: !Ref ProwlerResults + Path: "prowler" + NamespaceType: NONE + Packaging: NONE + OverrideArtifactName: False + EncryptionDisabled: False + LogsConfig: # S3/logs/pipeline/ + CloudWatchLogs: + Status: ENABLED + GroupName: "audit/prowler" + StreamName: "codebuild_runs" + S3Logs: + Status: DISABLED + # Location: !Sub "${ProwlerResults.Arn}/codebuild_run_logs" + EncryptionDisabled: False + BadgeEnabled: False + Tags: + - Key: "data-type" + Value: "it-audit:sensitive" + - Key: "data-public" + Value: "NO" + Cache: + Type: "NO_CACHE" + Source: + Type: NO_SOURCE + BuildSpec: | + version: 0.2 + env: + parameter-store: + PROWL_CHECK_GROUP: /audit/prowler/config/check_group + PROWL_MASTER_ACCOUNTS: /audit/prowler/config/orgmaster_accounts + PROWL_STANDALONE_ACCOUNTS: /audit/prowler/config/standalone_accounts + PROWL_SKIP_ACCOUNTS: /audit/prowler/config/skip_accounts + PROWL_AUDIT_ROLE: /audit/prowler/config/audit_role + PROWLER_REPO: /audit/prowler/config/gitrepo + PROWLER_CLONE_ARGS: /audit/prowler/config/gitcloneargs + phases: + install: + runtime-versions: + python: 3.7 + commands: + - aws --version + - git clone --depth 1 $PROWLER_REPO $PROWLER_CLONE_ARGS + pre_build: + commands: + - env | grep PROWL_ + - export OUTBASE=$(date -u +"out/diagnostics/%Y/%m/%d") + - export STAMP=$(date -u +"%Y%m%dT%H%M%SZ") + - mkdir -p $OUTBASE || true + - prowler/prowler -V + - aws sts get-caller-identity > ${OUTBASE}/${STAMP}-caller-id.json + build: + commands: + #### Run Prowler against this account, but don't fail the build + # - export PROWLER_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account') + # - /bin/bash prowler/prowler -g cislevel1 -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.cislevel1.csv || /bin/true + # - /bin/bash prowler/prowler -g forensics-ready -M csv -n -k > ${OUTBASE}/${STAMP}.${PROWLER_ACCOUNT_ID}.prowler.forensics-ready.csv || /bin/true + #### Run Prowler targeting all accounts in the configured organizations + - test -f prowler/util/config + - /bin/bash prowler/util/megaprowler.sh out + finally: + - ps axuwww | grep -E 'parallel|sem|prowler' + post_build: + commands: + - echo "attempting to collect any prowler credential reports ..." + - find /tmp/ -name prowler\* | xargs -I % cp % ${OUTDIAG} || true + artifacts: + files: + - '**/*' + discard-paths: no + base-directory: out + + + + + ProwlerAuditTriggerRole: + Type: AWS::IAM::Role + Properties: + # RoleName: Let cloudformation create this + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: events.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: AssumeRole-XA_AuditRole_Prowler + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - codebuild:StartBuild + Resource: + - !GetAtt ProwlerBuildProject.Arn + + ProwlerAuditTrigger: + Type: AWS::Events::Rule + Properties: + Description: !Sub "Execute Prowler audit every ${pAuditEveryXHours} hours" + Name: "ScheduledProwler" + RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn + ## Other ways to define scheduling + # ScheduleExpression: "cron(MM HH ? * * *)" + # ScheduleExpression: "cron(45 15 ? * * *)" + # ScheduleExpression: !Sub "rate( ${pAuditEveryXHours} hours)" + ScheduleExpression: !Sub "rate(${pAuditEveryXHours} hours)" + State: ENABLED + Targets: + - Arn: !GetAtt ProwlerBuildProject.Arn + Id: 'ScheduledProwler' + RoleArn: !GetAtt ProwlerAuditTriggerRole.Arn + + ProwlerConfigCheckGroup: + Type: AWS::SSM::Parameter + Properties: + Description: "Name of the prowler check group to use" + Name: "/audit/prowler/config/check_group" + Type: "String" + Value: !Ref pProwlerCheckGroup + + ProwlerConfigMasterAccounts: + Type: AWS::SSM::Parameter + Properties: + Description: "List of organization master accounts" + Name: "/audit/prowler/config/orgmaster_accounts" + Type: "String" + Value: !Ref pOrgMasterAccounts + + ProwlerConfigStandAloneAccounts: + Type: AWS::SSM::Parameter + Properties: + Description: "List of stand-alone accounts" + Name: "/audit/prowler/config/standalone_accounts" + Type: "String" + Value: !Ref pStandAloneAccounts + + ProwlerConfigSkipAccounts: + Type: AWS::SSM::Parameter + Properties: + Description: "List of skipped organization member accounts" + Name: "/audit/prowler/config/skip_accounts" + Type: "String" + Value: !Ref pOrgExcludedAccounts + + ProwlerConfigAuditRole: + Type: AWS::SSM::Parameter + Properties: + Description: "Role used to audit target accounts" + Name: "/audit/prowler/config/audit_role" + Type: "String" + Value: !Ref pAuditRolePathName + + ProwlerConfigGitRepo: + Type: AWS::SSM::Parameter + Properties: + Description: "Git repository where prowler is gathered" + Name: "/audit/prowler/config/gitrepo" + Type: "String" + Value: !Ref pCustomProwlerRepo + + ProwlerConfigGitCloneArgs: + Type: AWS::SSM::Parameter + Properties: + Description: "Git clone arguments" + Name: "/audit/prowler/config/gitcloneargs" + Type: "String" + Value: !Ref pCustomProwlerCloneArgs + + + # -- Conditional "cUseAthena" + # Athena + # QuickSight + # ??? + + +Outputs: + ResultsBucket: + Description: S3 Bucket with Prowler Results, Logs, Configs + Value: !Ref ProwlerResults diff --git a/util/multi-account/config b/util/multi-account/config new file mode 100644 index 00000000..12c35492 --- /dev/null +++ b/util/multi-account/config @@ -0,0 +1,33 @@ +#!/bin/bash +########### CODEBUILD CONFIGURATION ################## +# shellcheck disable=SC2034 +## Collect environment parameters set by buildspec +CHECKGROUP=${PROWL_CHECK_GROUP} + +if [ "none" == "${PROWL_MASTER_ACCOUNTS}" ]; then + ORG_MASTERS="" +else + ORG_MASTERS=$(echo "${PROWL_MASTER_ACCOUNTS}" | tr "," " ") +fi + +if [ "none" == "${PROWL_STANDALONE_ACCOUNTS}" ]; then + STANDALONE_ACCOUNTS="" +else + STANDALONE_ACCOUNTS=$(echo "${PROWL_STANDALONE_ACCOUNTS}" | tr "," " ") +fi + +if [ "none" == "${PROWL_SKIP_ACCOUNTS}" ]; then + SKIP_ACCOUNTS_REGEX='^$' +else + skip_inside=$(echo "${PROWL_SKIP_ACCOUNTS}" | tr "," "|") + # shellcheck disable=SC2116 + SKIP_ACCOUNTS_REGEX=$(echo "(${skip_inside})" ) +fi + +AUDIT_ROLE=${PROWL_AUDIT_ROLE} + +# Adjust if you clone prowler from somewhere other than the default location +PROWLER='prowler/prowler' + +# Change this if you want to ensure it breaks in code build +CREDSOURCE='EcsContainer' diff --git a/util/multi-account/megaprowler.sh b/util/multi-account/megaprowler.sh new file mode 100644 index 00000000..6c4a90f1 --- /dev/null +++ b/util/multi-account/megaprowler.sh @@ -0,0 +1,202 @@ +#!/bin/bash + +BASEDIR=$(dirname "${0}") +# source the configuration data from "config" in this directory +if [[ -f "${BASEDIR}/config" ]]; then + # shellcheck disable=SC1090 + . "${BASEDIR}/config" + +else + echo "CONFIG file missing - ${BASEDIR}/config" + exit 255 +fi + +## Check Environment variables which are set by config +if [[ "${ORG_MASTERS}X" == "X" && "${STANDALONE_ACCOUNTS}X" == "X" ]]; then + echo "No audit targets specified. Failing." + exit 15 +fi +if [[ -z $SKIP_ACCOUNTS_REGEX ]]; then + SKIP_ACCOUNTS_REGEX="" +fi + +if [[ -z $CHECKGROUP ]]; then + echo "Missing check group from config file" + exit 255 +fi +if [[ -z $AUDIT_ROLE ]]; then + echo "Missing audit role from config file" + exit 255 +fi + +## ======================================================================================== + +## Check Arguments +if [ $# -lt 1 ]; then + echo "NEED AN OUTPUT DIRECTORY" + exit 2 +else + if [[ -d $1 && -w $1 ]]; then + OUTBASE=$1 + else + echo "Output directory missing or write-protected" + exit 1 + fi +fi + + +## Check Requirements +if [[ -x $(command -v aws) ]]; then + aws --version +else + echo "AWS CLI is not in PATH ... giving up" + exit 4 +fi + +if [[ -x $(command -v jq) ]]; then + jq --version +else + echo "JQ is not in PATH ... giving up" + exit 4 +fi + +# Ensure AWS Credentials are present in environment +if [[ -z $CREDSOURCE ]]; then + echo "No source for base credentials ... giving up" + exit 5 +fi + +if [[ -f ${PROWLER} && -x ${PROWLER} ]]; then + ${PROWLER} -V +else + echo "Unable to execute prowler from ${PROWLER}" + exit 3 +fi + + +## Preflight checks complete + +DAYPATH=$(date -u +%Y/%m/%d) +STAMP=$(date -u +%Y%m%dT%H%M%SZ) +## Create output subdirs +OUTDATA="${OUTBASE}/data/${DAYPATH}" +OUTLOGS="${OUTBASE}/logs/${DAYPATH}" +mkdir -p "${OUTDATA}" "${OUTLOGS}" + + +if [[ -x $(command -v parallel) ]]; then + # Note: the "standard" codebuild container includes parallel + echo "Using GNU sem/parallel, with NCPU+4 jobs" + parallel --citation > /dev/null 2> /dev/null + PARALLEL_START="parallel --semaphore --fg --id p_${STAMP} --jobs +4 --env AWS_SHARED_CREDENTIALS_FILE" + PARALLEL_START_SUFFIX='' + PARALLEL_END="parallel --semaphore --wait --id p_${STAMP}" +else + echo "Consider installing GNU Parallel to avoid punishing your system" + PARALLEL_START='' + PARALLEL_START_SUFFIX=' &' + # shellcheck disable=SC2089 + PARALLEL_END="echo 'WAITING BLINDLY FOR PROCESSES TO COMPLETE'; wait ; sleep 30 ; wait" +fi + +echo "Execution Timestamp: ${STAMP}" + +ALL_ACCOUNTS="" + + +# Create a temporary credential file +AWS_MASTERS_CREDENTIALS_FILE=$(mktemp -t prowler.masters-XXXXXX) +echo "Preparing Credentials ${AWS_MASTERS_CREDENTIALS_FILE} ( ${CREDSOURCE} )" +echo "# Master Credentials ${STAMP}" >> "${AWS_MASTERS_CREDENTIALS_FILE}" +echo "" >> "${AWS_MASTERS_CREDENTIALS_FILE}" + +AWS_TARGETS_CREDENTIALS_FILE=$(mktemp -t prowler.targets-XXXXXX) +echo "Preparing Credentials ${AWS_TARGETS_CREDENTIALS_FILE} ( ${CREDSOURCE} )" +echo "# Target Credentials ${STAMP}" >> "${AWS_TARGETS_CREDENTIALS_FILE}" +echo "" >> "${AWS_TARGETS_CREDENTIALS_FILE}" + + +## Visit the Organization Master accounts & build a list of all member accounts +export AWS_SHARED_CREDENTIALS_FILE=$AWS_MASTERS_CREDENTIALS_FILE +for org in $ORG_MASTERS ; do + echo -n "Preparing organization $org " + # create credential profile + { + echo "[audit_${org}]" + echo "role_arn = arn:aws:iam::${org}:role${AUDIT_ROLE}" + echo "credential_source = ${CREDSOURCE}" + echo "" + } >> "${AWS_MASTERS_CREDENTIALS_FILE}" + + # Get the Organization ID to use for output paths, collecting info, etc + org_id=$(aws --output json --profile "audit_${org}" organizations describe-organization | jq -r '.Organization.Id' ) + + echo "( $org_id )" + ORG_ID_LIST="${ORG_ID_LIST} ${org_id}" + + + # Build the list of all accounts in the organizations + aws --output json --profile "audit_${org}" organizations list-accounts > "${OUTLOGS}/${STAMP}-${org_id}-account-list.json" + # shellcheck disable=SC2002 + ORG_ACCOUNTS=$( cat "${OUTLOGS}/${STAMP}-${org_id}-account-list.json" | jq -r '.Accounts[].Id' | tr "\n" " ") + ALL_ACCOUNTS="${ALL_ACCOUNTS} ${ORG_ACCOUNTS}" + + # Add the Org's Accounts (including master) to the TARGETS_CREDENTIALS file + for target in $ORG_ACCOUNTS ; do + if echo "$target" | grep -qE "${SKIP_ACCOUNTS_REGEX}"; then + echo " skipping account ${target} ( ${org_id} )" + continue + fi + # echo " ${org_id}_${target}" + { + echo "[${org_id}_${target}]" + echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" + echo "credential_source = ${CREDSOURCE}" + echo "" + } >> "${AWS_TARGETS_CREDENTIALS_FILE}" + done + +done + +# Prepare credentials for standalone accounts +if [[ "" != "${STANDALONE_ACCOUNTS}" ]] ; then + # mkdir -p ${OUTBASE}/data/standalone/${DAYPATH} ${OUTBASE}/logs/standalone/${DAYPATH} + for target in $STANDALONE_ACCOUNTS ; do + echo "Preparing account ${target} ( standalone )" + { + echo "[standalone_${target}]" + echo "role_arn = arn:aws:iam::${target}:role${AUDIT_ROLE}" + echo "credential_source = ${CREDSOURCE}" + echo "" + } >> "${AWS_TARGETS_CREDENTIALS_FILE}" + done + ALL_ACCOUNTS="${ALL_ACCOUNTS} ${STANDALONE_ACCOUNTS}" +fi + +# grep -E '^\[' $AWS_MASTERS_CREDENTIALS_FILE $AWS_TARGETS_CREDENTIALS_FILE + + +# Switch to Target Credential Set +export AWS_SHARED_CREDENTIALS_FILE=${AWS_TARGETS_CREDENTIALS_FILE} + +## visit each target account +NUM_ACCOUNTS=$(grep -cE '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}") +echo "Launching ${CHECKGROUP} audit of ${NUM_ACCOUNTS} accounts" +for member in $(grep -E '^\[' "${AWS_TARGETS_CREDENTIALS_FILE}" | tr -d '][') ; do + ORG_ID=$(echo "$member" | cut -d'_' -f1) + ACCOUNT_NUM=$(echo "$member" | cut -d'_' -f2) + + # shellcheck disable=SC2086 + ${PARALLEL_START} "${PROWLER} -p ${member} -n -M csv -g ${CHECKGROUP} 2> ${OUTLOGS}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.log > ${OUTDATA}/${STAMP}-${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP}.csv ; echo \"${ORG_ID}-${ACCOUNT_NUM}-prowler-${CHECKGROUP} finished\" " ${PARALLEL_START_SUFFIX} +done + +echo -n "waiting for parallel threads to complete - " ; date +# shellcheck disable=SC2090 +${PARALLEL_END} + +echo "Completed ${CHECKGROUP} audit with stamp ${STAMP}" + +# mkdir -p ${OUTBASE}/logs/debug/${DAYPATH} +# cp $AWS_MASTERS_CREDENTIALS_FILE ${OUTLOGS}/${STAMP}-master_creds.txt +# cp $AWS_TARGETS_CREDENTIALS_FILE ${OUTLOGS}/${STAMP}-target_creds.txt +rm "$AWS_MASTERS_CREDENTIALS_FILE" "$AWS_TARGETS_CREDENTIALS_FILE"