Skip to content

AWS - Reference

This section documents the current AWS FOCUS workflow modules used by eraXplor.

eraXplor source code


Main Application Module

Entry Point

AWS service package for the eraXplor FOCUS workflow.

This package exposes the AWS CLI entry point implemented in core.services.aws.__main__ and the supporting utility modules under core.services.aws.utils.

main()

Orchestrates and manages dependencies of AWS FOCUS export workflow.

Source code in src/core/services/aws/__main__.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def main() -> None:
    """Orchestrates and manages dependencies of AWS FOCUS export workflow."""
    try:
        # Banner
        _banner_format, _copyright_notice = generate_banner()
        print(f"\n\n {termcolor.colored(_banner_format, color='green')}")
        print(f"{termcolor.colored(_copyright_notice, color='green')}", end="\n\n")

        # Parse CLI args
        arg_parser = parser().parse_args()

        # Resolve parser-managed values
        command_mode = parser_command_handler(arg_parser)
        aws_profile_name_input = parser_profile_handler(arg_parser)
        aws_region_input = parser_region_handler(arg_parser)
        stack_name_input = parser_stack_name_handler(arg_parser)
        focus_time_granularity = parser_granularity_handler(arg_parser)

        if command_mode == "configure":
            # Deploy or update FOCUS export stack
            stack_result = deploy_focus_stack(
                stack_name=stack_name_input,
                profile_name=aws_profile_name_input,
                region=aws_region_input,
                focus_time_granularity=focus_time_granularity,
            )
            print(json.dumps(stack_result, indent=4, default=str), end="\n\n")
            print(
                "FOCUS export configured successfully. "
                "Run again with '--command download' after data is available.",
                end="\n\n",
            )
            return

        if command_mode == "download":
            # Download generated FOCUS parquet files
            downloaded_files = download_parquet_files(
                profile_name=aws_profile_name_input,
                region=aws_region_input,
                stack_name=stack_name_input,
            )
            print(f"Downloaded {len(downloaded_files)} parquet file(s).", end="\n\n")
            return

        raise ValueError(
            f"Unsupported command mode '{command_mode}'. "
            "Use '--command configure' or '--command download'."
        )
    except ValueError as exc:
        print(f"Error: {exc}")
        sys.exit(2)
    except Exception as exc:
        print(f"Unexpected error: {exc}")
        sys.exit(1)

This is the primary CLI entry point for configuring and downloading AWS FOCUS exports.


Utility Modules

Shared utility helpers for eraXplor service packages.

banner()

Generates a banner and copyright notice for the eraXplor application.

Creates an ASCII art banner using the 'slant' font with the application name, along with a formatted copyright notice containing version information and contact details.

Returns:

Name Type Description
tuple callable

A tuple containing two strings: - banner_format (str): ASCII art banner with the text "eraXplor" - copyright_notice (str): Formatted copyright and version information

Example

banner_format, copyright_notice = banner() print(banner_format) print(copyright_notice)

Note

The copyright year is currently set to 2025 and should be updated annually. The version number reflects the current release version of the eraXplor package.

Source code in src/core/services/utils/banner_utils.py
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def banner():
    """
    Generates a banner and copyright notice for the eraXplor application.

    Creates an ASCII art banner using the 'slant' font with the application name,
    along with a formatted copyright notice containing version information and
    contact details.

    Returns:
        tuple: A tuple containing two strings:
            - banner_format (str): ASCII art banner with the text "eraXplor"
            - copyright_notice (str): Formatted copyright and version information

    Example:
        >>> banner_format, copyright_notice = banner()
        >>> print(banner_format)
        >>> print(copyright_notice)

    Note:
        The copyright year is currently set to 2025 and should be updated
        annually. The version number reflects the current release version
        of the eraXplor package.
    """

    copyright_notice = """╔══════════════════════════════════════════════════╗
║  © 2026 Mohamed Eraki                            ║
║  mohamed-ibrahim2021@outlook.com                 ║
║  Version: 4.0.0                                  ║
║  eraXplor - FinOps Cost exporter Tool            ║
╚══════════════════════════════════════════════════╝
    """
    banner_format = pyfiglet.figlet_format("eraXplor", font='slant')
    return banner_format, copyright_notice

Responsible for rendering styled ASCII banners and displaying copyright information used in the CLI interface.


Parser Utilities

Moudle for parsing command line arguments for cost export utility.

parser()

Parser for the cost export utility.

Source code in src/core/services/aws/utils/focus_parser_utils.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def parser():
    """Parser for the cost export utility."""

    arg_parser = argparse.ArgumentParser(
        description="Export AWS account cost data using AWS Cost Explorer API.",
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )
    arg_parser.add_argument(
        "-p",
        "--profile",
        type=str,
        required=False,
        default="default",
        help="AWS profile name to use for authentication.",
    )
    arg_parser.add_argument(
        "-r",
        "--region",
        type=str,
        required=False,
        default="us-east-1",
        help="AWS region to use for authentication.",
    )
    arg_parser.add_argument(
        "-g",
        "--granularity",
        type=str,
        required=False,
        choices=["HOURLY", "DAILY", "MONTHLY"],
        default="MONTHLY",
        help="Time granularity of the cost data.",
    )
    arg_parser.add_argument(
        "-s",
        "--stack-name",
        type=str,
        required=False,
        default="CID-DataExports-Source",
        help="CloudFormation stack name for the CID Data Exports deployment.",
    )
    arg_parser.add_argument(
        "-c",
        "--command",
        type=str,
        required=False,
        choices=["configure", "download"],
        default="configure",
        help="Run mode: configure stack or download parquet files.",
    )
    return arg_parser

parser_command_handler(arg_parser)

parser_command_handler

Handles command mode input from the user or sets a default value to "configure".

Parameters:

Name Type Description Default
arg_parser list[ArgumentParser]

The parser objects.

required

Returns:

Name Type Description
str str

Return a command_mode object holds the selected command mode.

Source code in src/core/services/aws/utils/focus_parser_utils.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def parser_command_handler(arg_parser: list[argparse.ArgumentParser]) -> str:
    """parser_command_handler

    Handles command mode input from the user or sets a default value to
    "configure".

    Args:
        arg_parser (list[argparse.ArgumentParser]): The parser objects.

    Returns:
        str: Return a `command_mode` object holds the selected command mode.
    """
    try:
        if arg_parser.command:
            command_mode = arg_parser.command
            return command_mode
        if arg_parser.command is None:  # set default value
            command_mode = "configure"
            return command_mode
    except ValueError as e:
        print(f"Error parsing command mode: {e}")
        return None
    except Exception as e:  # Pylint: disable=broad-except
        print(f"Unexpected error: {e}")
        return None

parser_granularity_handler(arg_parser)

parser_granularity_handler

Handles the granularity input from the user or sets a default value to "monthly".

Parameters:

Name Type Description Default
arg_parser list[ArgumentParser]

The parser objects.

required

Returns:

Name Type Description
str str

Return a granularity object holds the granularity value.

Source code in src/core/services/aws/utils/focus_parser_utils.py
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def parser_granularity_handler(arg_parser: list[argparse.ArgumentParser]) -> str:
    """parser_granularity_handler

    Handles the granularity input from the user or sets a default value to "monthly".

    Args:
        arg_parser (list[argparse.ArgumentParser]): The parser objects.

    Returns:
        str: Return a `granularity` object holds the granularity value.
    """
    try:
        if arg_parser.granularity:
            granularity = arg_parser.granularity
            return granularity
        if arg_parser.granularity is None:  # set default value
            granularity = "MONTHLY"
            return granularity
    except ValueError as e:
        print(f"Error parsing granularity: {e}")
        return None
    except Exception as e:  # Pylint: disable=broad-except
        print(f"Unexpected error: {e}")
        return None

parser_profile_handler(arg_parser)

parser_profile

Handles the profile input from the user or set a default value to "default".

Parameters:

Name Type Description Default
arg_parser list[ArgumentParser]

The parser objects.

required

Returns:

Name Type Description
str str

Returns a aws_profile_name_input object holds the profile name.

Source code in src/core/services/aws/utils/focus_parser_utils.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def parser_profile_handler(arg_parser: list[argparse.ArgumentParser]) -> str:
    """parser_profile

    Handles the profile input from the user or set a default value to "default".

    Args:
        arg_parser (list[argparse.ArgumentParser]): The parser objects.

    Returns:
        str: Returns a `aws_profile_name_input` object holds the profile name.
    """
    try:
        # Check if AWS Profile is provided via command line arguments
        if arg_parser.profile:
            aws_profile_name_input = arg_parser.profile
            return aws_profile_name_input
        if arg_parser.profile is None:  # set default value
            aws_profile_name_input = "default"
            return aws_profile_name_input

    except ValueError as e:
        print(f"Error parsing AWS profile: {e}")
        return None
    except Exception as e:  # Pylint: disable=broad-except
        print(f"Unexpected error: {e}")
        return None

parser_region_handler(arg_parser)

parser_region_handler

Handles the region input from the user or sets a default value to "us-east-1".

Parameters:

Name Type Description Default
arg_parser list[ArgumentParser]

The parser objects.

required

Returns:

Name Type Description
str str

Returns a aws_region_input object holds the region name.

Source code in src/core/services/aws/utils/focus_parser_utils.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
def parser_region_handler(arg_parser: list[argparse.ArgumentParser]) -> str:
    """parser_region_handler

    Handles the region input from the user or sets a default value to "us-east-1".

    Args:
        arg_parser (list[argparse.ArgumentParser]): The parser objects.

    Returns:
        str: Returns a `aws_region_input` object holds the region name.
    """
    try:
        if arg_parser.region:
            aws_region_input = arg_parser.region
            return aws_region_input
        if arg_parser.region is None:  # set default value
            aws_region_input = "us-east-1"
            return aws_region_input
    except ValueError as e:
        print(f"Error parsing AWS region: {e}")
        return None
    except Exception as e:  # Pylint: disable=broad-except
        print(f"Unexpected error: {e}")
        return None

parser_stack_name_handler(arg_parser)

parser_stack_name_handler

Handles the stack name input from the user or sets a default value to "CID-DataExports-Source".

Parameters:

Name Type Description Default
arg_parser list[ArgumentParser]

The parser objects.

required

Returns:

Name Type Description
str str

Returns a stack_name object holds the CloudFormation stack name.

Source code in src/core/services/aws/utils/focus_parser_utils.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def parser_stack_name_handler(arg_parser: list[argparse.ArgumentParser]) -> str:
    """parser_stack_name_handler

    Handles the stack name input from the user or sets a default value to
    "CID-DataExports-Source".

    Args:
        arg_parser (list[argparse.ArgumentParser]): The parser objects.

    Returns:
        str: Returns a `stack_name` object holds the CloudFormation stack name.
    """
    try:
        if arg_parser.stack_name:
            stack_name = arg_parser.stack_name
            return stack_name
        if arg_parser.stack_name is None:  # set default value
            stack_name = "CID-DataExports-Source"
            return stack_name
    except ValueError as e:
        print(f"Error parsing stack name: {e}")
        return None
    except Exception as e:  # Pylint: disable=broad-except
        print(f"Unexpected error: {e}")
        return None

Contains the command parsing helpers used to validate CLI input and normalize workflow options.


Export Stack Utilities

Utility functions for automating AWS Data Exports using CID CloudFormation.

build_focus_stack_parameters(destination_account_id, source_account_ids=None, resource_prefix='cid', focus_time_granularity='DAILY')

Build CloudFormation parameters for CID Data Exports stack (FOCUS only).

Parameters:

Name Type Description Default
destination_account_id str

AWS account ID where exports are delivered.

required
source_account_ids list[str] | None

AWS account IDs to collect exports from.

None
resource_prefix str

Prefix for all CID-created AWS resources.

'cid'
focus_time_granularity str

Export time granularity. One of HOURLY, DAILY, MONTHLY. Changing this after initial deployment requires a full stack redeployment and data purge. Defaults to MONTHLY.

'DAILY'
Source code in src/core/services/aws/utils/aws_focus_export_stack_utils.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def build_focus_stack_parameters(
	destination_account_id: str,
	source_account_ids: list[str] | None = None,
	resource_prefix: str = "cid",
	focus_time_granularity: str = "DAILY",
) -> list[dict[str, str]]:
	"""Build CloudFormation parameters for CID Data Exports stack (FOCUS only).

	Args:
		destination_account_id: AWS account ID where exports are delivered.
		source_account_ids: AWS account IDs to collect exports from.
		resource_prefix: Prefix for all CID-created AWS resources.
		focus_time_granularity: Export time granularity. One of HOURLY, DAILY,
			MONTHLY. Changing this after initial deployment requires a full stack
			redeployment and data purge. Defaults to MONTHLY.
	"""
	parameters = [
		{"ParameterKey": "DestinationAccountId", "ParameterValue": destination_account_id},
		{"ParameterKey": "ResourcePrefix", "ParameterValue": resource_prefix},
		{"ParameterKey": "ManageCUR2", "ParameterValue": "no"},
		{"ParameterKey": "ManageFOCUS", "ParameterValue": "yes"},
		{"ParameterKey": "ManageCOH", "ParameterValue": "no"},
		{"ParameterKey": "ManageCarbon", "ParameterValue": "no"},
		{"ParameterKey": "LegacyLocalBucket", "ParameterValue": "no"},
		{"ParameterKey": "FOCUSTimeGranularity", "ParameterValue": focus_time_granularity},
	]

	if source_account_ids:
		parameters.append(
			{
				"ParameterKey": "SourceAccountIds",
				"ParameterValue": ",".join(source_account_ids),
			}
		)

	return parameters

deploy_focus_stack(stack_name='CID-DataExports-Source', profile_name='default', region='us-east-1', destination_account_id=None, source_account_ids=None, resource_prefix='cid', focus_time_granularity='MONTHLY', wait=True)

Deploy or update the CID Data Exports CloudFormation stack for FOCUS exports.

This uses the AWS-managed CloudFormation template from the Cloud Intelligence Dashboards guidance and enables only FOCUS export management.

Full list of CloudFormation template parameters and their defaults:

Parameter Template default This utility
DestinationAccountId (required) current account or arg
ResourcePrefix "cid" "cid"
ManageCUR2 (required) yes/no "no" (FOCUS only)
ManageFOCUS (required) yes/no "yes"
ManageCOH (required) yes/no "no"
ManageCarbon (required) yes/no "no"
SourceAccountIds (optional, comma list) current account or arg
FOCUSTimeGranularity "HOURLY" "MONTHLY" (configurable)
CUR2TimeGranularity "HOURLY" not passed (not used)
LegacyLocalBucket "yes" "no" (fresh deployments)
SecondaryDestinationBucket "" not passed (not used)
LakeFormationEnabled "no" not passed (template default)
EnableSCAD yes/no not passed (template default)
EnableIAMPrincipalData yes/no not passed (template default)
RolePath "/" not passed (template default)
AddScheduleForBlockingWrite "no" not passed (template default)
DisableWriteCronSchedule "0 1 * * ? *" not passed (template default)
EnableWriteCronSchedule "0 3 * * ? *" not passed (template default)

Note: changing focus_time_granularity on an existing stack requires a full stack redeployment, data purge in the destination bucket, and a backfill request to AWS. Do not change it lightly after initial deployment.

Source code in src/core/services/aws/utils/aws_focus_export_stack_utils.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
def deploy_focus_stack(
	stack_name: str = "CID-DataExports-Source",
	profile_name: str = "default",
	region: str = "us-east-1",
	destination_account_id: str | None = None,
	source_account_ids: list[str] | None = None,
	resource_prefix: str = "cid",
	focus_time_granularity: str = "MONTHLY",
	wait: bool = True,
) -> dict[str, Any]:
	"""
	Deploy or update the CID Data Exports CloudFormation stack for FOCUS exports.

	This uses the AWS-managed CloudFormation template from the Cloud Intelligence
	Dashboards guidance and enables only FOCUS export management.

	Full list of CloudFormation template parameters and their defaults:

	Parameter                  | Template default        | This utility
	---------------------------|-------------------------|------------------------------
	DestinationAccountId       | (required)              | current account or arg
	ResourcePrefix             | "cid"                   | "cid"
	ManageCUR2                 | (required) yes/no       | "no"  (FOCUS only)
	ManageFOCUS                | (required) yes/no       | "yes"
	ManageCOH                  | (required) yes/no       | "no"
	ManageCarbon               | (required) yes/no       | "no"
	SourceAccountIds           | (optional, comma list)  | current account or arg
	FOCUSTimeGranularity       | "HOURLY"                | "MONTHLY" (configurable)
	CUR2TimeGranularity        | "HOURLY"                | not passed (not used)
	LegacyLocalBucket          | "yes"                   | "no"  (fresh deployments)
	SecondaryDestinationBucket | ""                      | not passed (not used)
	LakeFormationEnabled       | "no"                    | not passed (template default)
	EnableSCAD                 | yes/no                  | not passed (template default)
	EnableIAMPrincipalData     | yes/no                  | not passed (template default)
	RolePath                   | "/"                     | not passed (template default)
	AddScheduleForBlockingWrite| "no"                    | not passed (template default)
	DisableWriteCronSchedule   | "0 1 * * ? *"           | not passed (template default)
	EnableWriteCronSchedule    | "0 3 * * ? *"           | not passed (template default)

	Note: changing focus_time_granularity on an existing stack requires a full
	stack redeployment, data purge in the destination bucket, and a backfill
	request to AWS. Do not change it lightly after initial deployment.
	"""
	_validate_stack_name(stack_name)

	session = boto3.Session(profile_name=profile_name, region_name=region)
	cloudformation = session.client("cloudformation", region_name=region)

	account_id = destination_account_id or _get_current_account_id(session)
	if source_account_ids is None:
		source_account_ids = [account_id]

	parameters = build_focus_stack_parameters(
		destination_account_id=account_id,
		source_account_ids=source_account_ids,
		resource_prefix=resource_prefix,
		focus_time_granularity=focus_time_granularity,
	)

	stack_exists = True
	try:
		cloudformation.describe_stacks(StackName=stack_name)
	except ClientError as exc:
		if "does not exist" in str(exc):
			stack_exists = False
		else:
			raise

	if not stack_exists:
		destination_bucket_name = f"{resource_prefix}-{account_id}-data-exports"
		if _is_bucket_name_in_use(session, region, destination_bucket_name):
			raise ValueError(
				f"Resource conflict: AWS::S3::Bucket '{destination_bucket_name}' "
				"already exists. "
				f"Stack '{stack_name}' does not exist, so this run is in create mode "
				"(not update mode). "
				"Use the existing stack name to update, choose a different "
				"resource_prefix, or delete/empty old retained resources first."
			)

		print(f"Creating CloudFormation stack '{stack_name}' for FOCUS export automation...")
		response = cloudformation.create_stack(
			StackName=stack_name,
			TemplateURL=CID_DATA_EXPORTS_TEMPLATE_URL,
			Capabilities=["CAPABILITY_NAMED_IAM"],
			Parameters=parameters,
		)
		action = "create"
	else:
		print(f"Updating CloudFormation stack '{stack_name}' for FOCUS export automation...")
		try:
			response = cloudformation.update_stack(
				StackName=stack_name,
				TemplateURL=CID_DATA_EXPORTS_TEMPLATE_URL,
				Capabilities=["CAPABILITY_NAMED_IAM"],
				Parameters=parameters,
			)
			action = "update"
		except ClientError as exc:
			message = str(exc)
			if "No updates are to be performed" in message:
				stack = cloudformation.describe_stacks(StackName=stack_name)["Stacks"][0]
				return {
					"action": "none",
					"stack_name": stack_name,
					"stack_id": stack["StackId"],
					"stack_status": stack["StackStatus"],
					"parameters": parameters,
				}
			raise

	stack_id = response["StackId"]

	if wait:
		waiter_name = "stack_create_complete" if action == "create" else "stack_update_complete"
		try:
			cloudformation.get_waiter(waiter_name).wait(StackName=stack_name)
		except WaiterError as exc:
			stack = cloudformation.describe_stacks(StackName=stack_name)["Stacks"][0]
			stack_status = stack.get("StackStatus", "UNKNOWN")
			stack_status_reason = stack.get("StackStatusReason", "")
			failure_reason = _get_stack_failure_reason(cloudformation, stack_name)
			raise ValueError(
				f"CloudFormation stack '{stack_name}' {action} failed with status "
				f"'{stack_status}'. "
				f"Reason: {stack_status_reason or failure_reason}. "
				"Review stack events in AWS Console, fix the issue, then rerun configure."
			) from exc

	stack = cloudformation.describe_stacks(StackName=stack_name)["Stacks"][0]
	return {
		"action": action,
		"stack_name": stack_name,
		"stack_id": stack_id,
		"stack_status": stack["StackStatus"],
		"parameters": parameters,
	}

Contains the CloudFormation orchestration helpers that create or update the AWS FOCUS export infrastructure.


Fetch Utilities

Module for fetching FOCUS Parquet files from AWS S3.

download_parquet_files(profile_name='default', region='us-east-1', stack_name='CID-DataExports-Source')

Downloads FOCUS Parquet files from AWS S3.

Parameters:

Name Type Description Default
profile_name str

AWS profile name.

'default'
region str

AWS region where the stack is deployed.

'us-east-1'
stack_name str

CloudFormation stack name that owns FOCUS resources.

'CID-DataExports-Source'

Returns:

Type Description
list[str]

List of paths to the downloaded Parquet files.

Source code in src/core/services/aws/utils/aws_focus_fetch.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def download_parquet_files(
	profile_name: str = "default",
	region: str = "us-east-1",
	stack_name: str = "CID-DataExports-Source",
) -> list[str]:
	"""
	Downloads FOCUS Parquet files from AWS S3.

	Args:
		profile_name: AWS profile name.
		region: AWS region where the stack is deployed.
		stack_name: CloudFormation stack name that owns FOCUS resources.

	Returns:
		List of paths to the downloaded Parquet files.
	"""

	local_download_path = "./downloaded_parquet_files"
	os.makedirs(local_download_path, exist_ok=True)

	session = boto3.Session(profile_name=profile_name, region_name=region)
	s3_client = session.client("s3", region_name=region)

	resolved_bucket_name = _resolve_focus_bucket_name(
		session=session,
		stack_name=stack_name,
		region=region,
	)
	resolved_folder_name = _resolve_focus_prefix(
		session=session,
		stack_name=stack_name,
		region=region,
	)
	prefix = resolved_folder_name.rstrip("/") + "/"

	print(
		f"Listing objects in bucket '{resolved_bucket_name}' under prefix '{prefix}'..."
	)

	downloaded_files = []
	paginator = s3_client.get_paginator("list_objects_v2")

	for page in paginator.paginate(Bucket=resolved_bucket_name, Prefix=prefix):
		for obj in page.get("Contents", []):
			key = obj["Key"]

			if key.endswith("/") or not key.lower().endswith(".parquet"):
				continue

			download_file_path = os.path.join(local_download_path, os.path.basename(key))
			s3_client.download_file(resolved_bucket_name, key, download_file_path)
			downloaded_files.append(download_file_path)
			print(f"Downloaded: {download_file_path}")

	if not downloaded_files:
		raise FileNotFoundError(
			"No Parquet files were found in the configured FOCUS bucket/prefix."
		)

	return downloaded_files

Provides functionality for downloading generated Parquet export files after the AWS export pipeline has produced them.