From 6487be413307cb4d870b197562884dbb963b5505 Mon Sep 17 00:00:00 2001 From: David Steinberg Date: Thu, 2 May 2024 10:10:21 -0700 Subject: [PATCH 1/2] Remove inventory workbook --- .../README.md | 128 ----- ...ntegrated-Inventory-Workbook-Template.xlsx | Bin 297074 -> 0 bytes .../deployment/inventory/__init__.py | 0 .../deployment/inventory/handler.py | 14 - .../deployment/inventory/mappers.py | 282 ---------- .../deployment/inventory/readers.py | 114 ---- .../deployment/inventory/reports.py | 84 --- .../package.sh | 8 - .../requirements.txt | 10 - .../sample_config_query_results/sample.json | 294 ----------- .../sample_config_query_results/sample2.json | 492 ------------------ .../sample_classic_elb.json | 69 --- .../sample_dynamo_table.json | 36 -- .../sample_ec2.json | 133 ----- .../sample_es.json | 108 ---- .../sample_lambda_function.json | 59 --- .../sample_rds_db.json | 94 ---- .../sample_rds_instance.json | 144 ----- .../sample_route_table.json | 69 --- .../sample_s3.json | 53 -- .../sample_v2elb.json | 37 -- .../sample_vpc.json | 89 ---- .../tests/test_dynamo_table_mapper.py | 34 -- .../tests/test_ec2_mapper.py | 95 ---- .../tests/test_elastic_search_mapper.py | 36 -- .../tests/test_elb_mapper.py | 100 ---- .../tests/test_inventory_reader.py | 99 ---- .../tests/test_lambda_mapper.py | 35 -- .../tests/test_rds_mapper.py | 80 --- .../tests/test_reports.py | 45 -- .../tests/test_s3_mapper.py | 48 -- .../tests/test_vpc_mapper.py | 27 - 32 files changed, 2916 deletions(-) delete mode 100644 fedramp-integrated-inventory-workbook/README.md delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/SSP-A13-FedRAMP-Integrated-Inventory-Workbook-Template.xlsx delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/__init__.py delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/handler.py delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/mappers.py delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/readers.py delete mode 100644 fedramp-integrated-inventory-workbook/deployment/inventory/reports.py delete mode 100755 fedramp-integrated-inventory-workbook/package.sh delete mode 100644 fedramp-integrated-inventory-workbook/requirements.txt delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample2.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_classic_elb.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_dynamo_table.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_ec2.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_es.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_lambda_function.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_db.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_instance.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_route_table.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_s3.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_v2elb.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_vpc.json delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_dynamo_table_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_ec2_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_elastic_search_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_elb_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_inventory_reader.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_lambda_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_rds_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_reports.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_s3_mapper.py delete mode 100644 fedramp-integrated-inventory-workbook/tests/test_vpc_mapper.py diff --git a/fedramp-integrated-inventory-workbook/README.md b/fedramp-integrated-inventory-workbook/README.md deleted file mode 100644 index 2415dfb7..00000000 --- a/fedramp-integrated-inventory-workbook/README.md +++ /dev/null @@ -1,128 +0,0 @@ -# FedRAMP Integrated Inventory Workbook Generator - -## License - -Additionally, this project installs the following software for the purposes of deploying and running the labs into the lab environment: - -* [openpyxl](https://openpyxl.readthedocs.io/en/stable/index.html) package. Python open source software is provided under the MIT/Expat License. -* [pytest](https://docs.pytest.org/en/latest/) package. Python open source software is provided under the MIT License. -* [pylint](https://pylint.readthedocs.io/en/latest/) package. Python open source software is provided under the GNU General Public License. -* [mypy](http://mypy-lang.org/) package. Python open source software is provided under the MIT License. -* [autopep8](https://github.com/hhatto/autopep8) package. Python open source software is provided under the MIT License. -* [callee](https://callee.readthedocs.io/en/latest/reference/general.html) package. Python open source software is provided under the BSD 3-Clause "New" or "Revised" License. - -## Overview - -This sample shows how you can create a Lambda function to retrieve inventory information to create the integrated inventory spreadsheet which can be used as a separate attachment to the FedRAMP System Security Plan (SSP) and is the repository associated to the [Automating creation of a FedRAMP Integrated Inventory Workbook](https://aws.amazon.com/blogs/publicsector/automating-creation-fedramp-integrated-inventory-workbook/) blog post. The spreadsheet template can be found [here](https://www.fedramp.gov/new-integrated-inventory-template/). - -This sample populates the inventory spreadsheet with a point in time view of a subset of all AWS resources spanning multiple AWS accounts. The following resource types are currently supported AWS::EC2::Instance, AWS::ElasticLoadBalancingV2::LoadBalancer, AWS::ElasticLoadBalancing::LoadBalancer, AWS::DynamoDB::Table, AWS::RDS::DBInstance. - -There are other assets that must be tracked in the spreadsheet (e.g. software running on EC2 instances/containers) which this sample does not gather. The design does lend itself to be extended to gather inventory information from multiple sources for various resource types. - -## Contents - -This project follows the [src project structure](https://blog.ionelmc.ro/2014/05/25/python-packaging/). In other words, this: -``` -├─ src -│ └─ packagename -│ ├─ __init__.py -│ └─ ... -├─ tests -│ └─ ... -``` - -Additionally, here are notes of other key files/folders not typically found in a Python project: - -* **package.sh** - This script bundles the package so that it can be uploaded to Lambda. However, a Lambda package .zip file is already included with the repository. This requires the setup of a virtual environment using pyenv. [AWS Serverless Application Model](https://aws.amazon.com/serverless/sam/) was not used in an effort to minimize the number of concepts introduced. - -## Running the Code -The code was developed using Python 3.8, pyenv, and pipenv. After cloning the repository locally, create a virtualenv however you prefer. Both a requirements.txt file and Pipfile have been provided, for example if you have Python 3.8 installed and set at the current version, you can run the following commands in the project directory: - -``` bash -python -m venv . -source ./bin/activate -``` - -Install the package, its dependencies and dev dependencies. Dev dependencies are not included in the requirements.txt as pipenv is used for dependency management, and requirements.txt was created without including dev dependencies. For ease of getting up an running though, you can execute the following commands: - -``` bash -python -m pip install -r requirements.txt -python -m pip install pytest -python -m pip install callee -cd src -python -m pytest -v -s ../tests -``` - -If you've got everything installed correctly, you should see output similar to: - -![Unit Test Results](./docs/TestResults.png) - -### Development -The project was developed using Visual Studio Code and the .vscode directory with three launch configuration is included. Among them is "Run All Tests" configuration which can be used to run all unit tests in the project. Unit tests mock out calls to AWS services so you do not need to worry about tests using the services when executed. A .env.sample file is included which you can use to set the environment variables used by Visual Studio Code. If the .env file is not recognized by Visual Studio Code, ensure that the "python.envFile" setting is set to "${workspaceFolder}/.env". - -### Environment Variables - -* **AWS_REGION** - AWS region from which the AWS Config resources will be queried -* **ACCOUNT_LIST** - JSON document containing the list of accounts that need to be queried for inventory with the following structure -``` json -[ { "name": , "id": } ] -``` -* **CROSS_ACCOUNT_ROLE_NAME** - Name of the role that will be assumed on the accounts where inventory needs to be retrieved -* **REPORT_TARGET_BUCKET_PATH** - Prefix of the S3 object key for the report. Similar to foler path to where the report will be uploaded -* **REPORT_TARGET_BUCKET_NAME** - Name of the S3 bucket where report will be uploaded (without "s3://") -* **LOG_LEVEL (Optional)** - Default of INFO. The package uses the STL's logger module and any of the [log levels](https://docs.python.org/3/library/logging.html#levels) available there can be used. -* **REPORT_WORKSHEET_NAME (Optional)** - Default of "Inventory". Name of the worksheet in the "SSP-A13-FedRAMP-Integrated-Inventory-Workbook-Template" spreadsheet where inventory data will be populated. -* **REPORT_WORKSHEET_FIRST_WRITEABLE_ROW_NUMBER** (Optional) - Default of 6. Row number (not index) of where inventory data will start to be populated. - -## Design -This section contains the design details of this package. - -### Items In Scope -* Gather inventory information from AWS Config and deliver to S3 - -### Items Out-of-Scope / Possible Next Steps -* Errors while retrieving inventory from AWS accounts are logged as errors but processing continues. Raising a CloudWatch event for these errors so that alerts can be created could be a next step. -* Account list is provided via an Environment Variable, using either AWS Organizations to gather the list of member accounts or using a centralized store where this list is maintained could be a next step. -* Publishing metrics is out of scope -* Software/Container inventory is out of scope -* Use of structured logging is out of scope -* Access to the report is out of scope. This project merely drops the file in S3 -* Code Coverage, and CI/CD pipeline are out of scope -* Using [AWS Serverless Application Model](https://aws.amazon.com/serverless/sam/) is out of scope - -### Conceptual Design -![Conceptual Design](./docs/ConceptualDesign.png) - -The above diagram depics the conceptual design. As you can see, the Lambda function can be triggered by a CloudWatch event, gathers inventory information from AWS Config and persists the Workbook into a S3 bucket. - -### Static Relationships -![Class Diagram](./docs/StaticClassDiagram.png) - -The above diagram shows the modules that make up the inventory package and relationships between them. - -Classes in the Readers and Reports modules implement the [Command Handler pattern](https://blogs.cuttingedge.it/steven/posts/2011/meanwhile-on-the-command-side-of-my-architecture/). To keep things simple and given that dependency injection is not used, method arguments are not represented as Command classes. - -The Handler module contains the Lambda entry point that acts as the coordinator of the AwsConfigInventoryReader which is responsible for retrieving inventory information, CreateReportCommandHandler which is responsible for creating the inventory report spreadsheet, and the DeliverReportCommandHandler which is responsible for uploading the spreadsheet to S3. - -The Mappers module is composed of a class hierarchy that implements the [Data Mapper pattern](https://martinfowler.com/eaaCatalog/dataMapper.html), providing a well known extensibility point for adding additional classes to map new resource types. The result of data mapping is a list of InventoryData instances. The goal is to normalize the various data structures retrieved from AWS Config into a single type which can then be used by the CreateReportCommandHandler to populate the inventory spreadsheet. - -### Dynamic Behavior -The following section details this package's runtime behavior of the major components - -#### Report Generation -![Report Generation Sequence Diagram](./docs/SequenceOverview.png) - -Before we get into the details, lets look at sequence of steps and the classes that the Lambda Handler module uses to create the inventory report. As you can see, the Handler needs to directly interact with only three classes, AwsConfiInventoryReader, CreateReportCommandHandler and DeliverReportCommandHandler, whose names imply their responsibility. Now let's take a bit of a more detailed look at the call sequence. - -![Report Generation Sequence Diagram](./docs/ReportGenerationSequenceDiagram.png) - -The above sequence diagram depicts the report generation process in its entirety. Most of the complexity is centered around the retrieval and mapping the AWS Config data into a normalized structure. It is the AwsConfigInventoryReader's resposibility to return this normalized structure. As AwsConfigInventoryReader iterates through each AWS Config resource, it queries the list of DataMappers to determine which can handle the item. Once all AWS Config resources have been mapped into an InventoryData instance, the list is returned to the Handler. - -The Handler subsequently calls the CreateReportCommandHandler and DeliverReportCommandHandler to create the inventory spreadsheet and upload it to S3 respectively. - -#### Error Handling -![Error Handling Sequence Diagram](./docs/ErrorHandlingSequenceDiagram.png) - -As depicted above, errors encountered during the retrieval of inventory information from AWS Config, are logged; however, processing continues. Below is a screenshot from CloudWatch showing the log entry with specific sections of the log entry highlighted. - -![Error Log Entry](docs/ErrorLogEntry.png) \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/deployment/inventory/SSP-A13-FedRAMP-Integrated-Inventory-Workbook-Template.xlsx b/fedramp-integrated-inventory-workbook/deployment/inventory/SSP-A13-FedRAMP-Integrated-Inventory-Workbook-Template.xlsx deleted file mode 100644 index 9cdb858981e3f2d039846f57ca37abc9347d5ad3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297074 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

n?%;MCFcpsWZ1|ra( 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 str: - return next((tag["value"] for tag in tags if "key" in tag and tag["key"].casefold() == tag_name.casefold()), '') - - -class InventoryData: - def __init__(self, *, asset_type=None, unique_id=None, ip_address=None, location=None, is_virtual=None, - authenticated_scan_planned=None, dns_name=None, mac_address=None, baseline_config=None, hardware_model=None, - is_public=None, network_id=None, owner=None, software_product_name=None, software_vendor=None, comments=None, - in_latest_scan=None, purpose=None, asset_tag=None): - self.asset_type = asset_type - self.unique_id = unique_id - self.ip_address = ip_address - self.location = location - self.is_virtual = is_virtual - self.authenticated_scan_planned = authenticated_scan_planned - self.dns_name = dns_name - self.mac_address = mac_address - self.baseline_config = baseline_config - self.hardware_model = hardware_model - self.is_public = is_public - self.network_id = network_id - self.owner = owner - self.software_product_name = software_product_name - self.software_vendor = software_vendor - self.comments = comments - self.in_latest_scan = in_latest_scan - self.purpose = purpose - self.asset_tag = asset_tag - - -class DataMapper(ABC): - REQUIRES_MANUAL_INPUT = "TODO" - - @abstractmethod - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - pass - - @abstractmethod - def _get_supported_resource_type(self) -> List[str]: - pass - - def can_map(self, resource_type: str) -> bool: - return resource_type in self._get_supported_resource_type() - - def map(self, config_resource: dict) -> List[InventoryData]: - if not self.can_map(config_resource["resourceType"]): - return [] - - mapped_data = [] - - _logger.debug(f"mapping {config_resource['resourceType']}") - _logger.debug(config_resource) - mapped_data.extend(self._do_mapping(config_resource)) - - _logger.debug(f"mapping resulted in a total of {len(mapped_data)} rows") - - return mapped_data - - -class EC2DataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::EC2::Instance"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - ec2_data_list: List[InventoryData] = [] - - for nic in config_resource["configuration"]["networkInterfaces"]: - for ipAddress in nic["privateIpAddresses"]: - ec2_data = {"asset_type": "EC2", - "unique_id": config_resource["configuration"]["instanceId"], - "ip_address": ipAddress["privateIpAddress"], - "is_virtual": "Yes", - "authenticated_scan_planned": "Yes", - "in_latest_scan": self.REQUIRES_MANUAL_INPUT, - "software_vendor": "AWS", - "mac_address": nic["macAddress"], - "baseline_config": config_resource["configuration"]["imageId"], - "hardware_model": config_resource["configuration"]["instanceType"], - "network_id": config_resource["configuration"]["vpcId"], - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner")} - - if (public_dns_name := config_resource["configuration"].get("publicDnsName")): - ec2_data["dns_name"] = public_dns_name - ec2_data["is_public"] = "Yes" - else: - ec2_data["dns_name"] = config_resource["configuration"]["privateDnsName"] - ec2_data["is_public"] = "No" - - if "association" in ipAddress: - # Add a publicIp address it the ip_address field if necessary - ec2_data["ip_address"] += "," + ipAddress["association"]["publicIp"] - - ec2_data_list.append(InventoryData(**ec2_data)) - - return ec2_data_list - - -class ElbDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::ElasticLoadBalancing::LoadBalancer", "AWS::ElasticLoadBalancingV2::LoadBalancer"] - - def _get_asset_type_name(self, config_resource: dict) -> str: - if config_resource["resourceType"] == "AWS::ElasticLoadBalancing::LoadBalancer": - return "Load Balancer-Classic" - else: - return f"Load Balancer-{config_resource['configuration']['type']}" - - def _get_ip_addresses(self, availabilityZones: dict) -> List[str]: - ip_addresses: List[str] = [] - - for availabilityZone in availabilityZones: - if load_balancer_addresses := availabilityZone.get("loadBalancerAddresses"): - for load_balancer_address in (load_balancer_address for load_balancer_address in load_balancer_addresses if - "ipAddress" in load_balancer_address): - ip_addresses.append(load_balancer_address["ipAddress"]) - - return ip_addresses - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data_list: List[InventoryData] = [] - - data = {"asset_type": self._get_asset_type_name(config_resource), - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "software_vendor": "AWS", - "is_public": "Yes" if config_resource["configuration"]["scheme"] == "internet-facing" else "No", - # Classic ELBs have key of "vpcid" while V2 ELBs have key of "vpcId" - "network_id": config_resource["configuration"]["vpcId"] if "vpcId" in config_resource["configuration"] else - config_resource["configuration"]["vpcid"], - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner")} - - if len(ip_addresses := self._get_ip_addresses(config_resource["configuration"]["availabilityZones"])) > 0: - for ip_address in ip_addresses: - data = copy.deepcopy(data) - - data["ip_address"] = ip_address - - data_list.append(InventoryData(**data)) - else: - data_list.append(InventoryData(**data)) - - return data_list - - -class RdsDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::RDS::DBInstance"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data = {"asset_type": "RDS", - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "software_vendor": "AWS", - "authenticated_scan_planned": "No", - "purpose": self.REQUIRES_MANUAL_INPUT, - "is_public": "Yes" if config_resource["configuration"]["publiclyAccessible"] else "No", - "hardware_model": config_resource["configuration"]["dBInstanceClass"], - "software_product_name": f"{config_resource['configuration']['engine']}-{config_resource['configuration']['engineVersion']}", - "network_id": config_resource['configuration']['dBSubnetGroup']['vpcId'] if "dBSubnetGroup" in config_resource[ - 'configuration'] else '', - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner"), - "location": config_resource["awsRegion"]} - - return [InventoryData(**data)] - - -class DynamoDbTableDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::DynamoDB::Table"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data = {"asset_type": "DynamoDB", - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "is_public": "No", - "software_vendor": "AWS", - "software_product_name": "DynamoDB", - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner")} - - return [InventoryData(**data)] - - -class S3DataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::S3::Bucket"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - - if "supplementaryConfiguration" in config_resource and "PublicAccessBlockConfiguration" in config_resource["supplementaryConfiguration"]: - # check if each of the block access config values are true, if so, then the bucket is not public - public_access_config = config_resource["supplementaryConfiguration"]["PublicAccessBlockConfiguration"] - is_public = "No" if all(public_access_config[key] for key in public_access_config) else "Yes" - else: - # if there is no PublicAccessBlockConfiguration then this bucket is public - is_public = "Yes" - - data = {"asset_type": "S3", - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "is_public": is_public, - "software_vendor": "AWS", - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner"), - "comments": "Encrypted" if "ServerSideEncryptionConfiguration" in config_resource["supplementaryConfiguration"] else "Not encrypted", - "location": config_resource["awsRegion"] - } - - return [InventoryData(**data)] - - -class VPCDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::EC2::VPC"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data = {"asset_type": "VPC", - "unique_id": config_resource["arn"], - "ip_address": config_resource["configuration"]["cidrBlock"], - "is_virtual": "Yes", - "is_public": "Yes", - "software_vendor": "AWS", - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "baseline_config": config_resource["configurationStateId"], - "network_id": config_resource["configuration"]["vpcId"], - "owner": _get_tag_value(config_resource["tags"], "owner"), - "location": config_resource["awsRegion"] - } - - return [InventoryData(**data)] - - -class LambdaDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::Lambda::Function"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data = {"asset_type": "Lambda Function", - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "is_public": "No", - "baseline_config": config_resource["configuration"]["runtime"], - "software_vendor": "Dockstore", - "software_product_name": "sha256: " + config_resource["configuration"]["codeSha256"], - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "purpose": self.REQUIRES_MANUAL_INPUT, - "owner": _get_tag_value(config_resource["tags"], "owner"), - "location": config_resource["awsRegion"] - } - - return [InventoryData(**data)] - - -class ElasticSearchDataMapper(DataMapper): - def _get_supported_resource_type(self) -> List[str]: - return ["AWS::Elasticsearch::Domain"] - - def _do_mapping(self, config_resource: dict) -> List[InventoryData]: - data = {"asset_type": "Elastic Search", - "unique_id": config_resource["arn"], - "is_virtual": "Yes", - "is_public": "No", - "baseline_config": config_resource["configuration"]["elasticsearchVersion"], - "software_vendor": "AWS", - "software_product_name": "Elastic Search", - "asset_tag": config_resource["resourceName"] if "resourceName" in config_resource else config_resource["resourceId"], - "owner": _get_tag_value(config_resource["tags"], "owner"), - "location": config_resource["awsRegion"]} - return [InventoryData(**data)] diff --git a/fedramp-integrated-inventory-workbook/deployment/inventory/readers.py b/fedramp-integrated-inventory-workbook/deployment/inventory/readers.py deleted file mode 100644 index e2bd8afc..00000000 --- a/fedramp-integrated-inventory-workbook/deployment/inventory/readers.py +++ /dev/null @@ -1,114 +0,0 @@ -import json -import logging -import os -from typing import Iterator, List, Optional -import boto3 -from botocore.exceptions import ClientError -from .mappers import DataMapper, EC2DataMapper, ElbDataMapper, DynamoDbTableDataMapper, InventoryData, RdsDataMapper, S3DataMapper, \ - VPCDataMapper, LambdaDataMapper, ElasticSearchDataMapper - -_logger = logging.getLogger("inventory.readers") -_logger.setLevel(os.environ.get("LOG_LEVEL", logging.INFO)) - - -class AwsConfigInventoryReader(): - def __init__(self, lambda_context, mappers=None): - if mappers is None: - mappers = [EC2DataMapper(), ElbDataMapper(), DynamoDbTableDataMapper(), RdsDataMapper(), S3DataMapper(), VPCDataMapper(), - LambdaDataMapper(), ElasticSearchDataMapper()] - self._lambda_context = lambda_context - self._mappers: List[DataMapper] = mappers - - # Moved into it's own method to make it easier to mock boto3 client - def _get_config_client(self, sts_response, region: str) -> boto3.client: - return boto3.client('config', - aws_access_key_id=sts_response.access_key, - aws_secret_access_key=sts_response.secret_key, - aws_session_token=sts_response.token, - region_name=region) - - def _get_resources_from_account(self, account_id: str, region_list: List[str]) -> Iterator[List[str]]: - try: - _logger.info(f"assuming role on account {account_id}") - - for region in region_list: - sts_response = boto3.Session().get_credentials() - config_client = self._get_config_client(sts_response, region) - - _logger.info(f"Querying resources on account {account_id} for region {region}") - - next_token: str = '' - while True: - resources_result = config_client.select_resource_config( - Expression="SELECT arn, resourceName, resourceId, resourceType, configuration, supplementaryConfiguration, configurationStateId, tags, awsRegion " - "WHERE resourceType IN ('AWS::EC2::Instance', 'AWS::ElasticLoadBalancingV2::LoadBalancer', " - "'AWS::ElasticLoadBalancing::LoadBalancer', 'AWS::RDS::DBInstance', " - "'AWS::Lambda::Function', 'AWS::EC2::VPC', 'AWS::S3::Bucket', 'AWS::Elasticsearch::Domain')", - NextToken=next_token) - - next_token = resources_result.get('NextToken', '') - results: List[str] = resources_result.get('Results', []) - - _logger.debug(f"Region {region} page returned {len(results)} and next token of '{next_token}'") - - yield results - - if not next_token: - break - - except ClientError as ex: - _logger.error("Received error: %s while retrieving resources from account %s, moving onto next account.", ex, account_id, - exc_info=True) - - yield [] - - def _get_aws_partition(self): - arn_parts = self._lambda_context.invoked_function_arn.split(":") - - return arn_parts[1] if len(arn_parts) >= 1 else '' - - def get_resources_from_all_accounts(self) -> List[InventoryData]: - _logger.info("starting retrieval of inventory from AWS Config") - - all_inventory: List[InventoryData] = [] - accounts = json.loads(os.environ["ACCOUNT_LIST"]) - - for account in accounts: - region_list = account["regions"] - _logger.info(f"retrieving inventory for account {account['id']} in regions {region_list}") - - for resource_list_page in self._get_resources_from_account(account["id"], region_list): - _logger.debug(f"current page of inventory contained {len(resource_list_page)} items from AWS Config") - - for raw_resource in resource_list_page: - resource: dict = json.loads(raw_resource) - - # One line item returned from AWS Config can result in multiple inventory line items (e.g. multiple IPs) - # Mappers that do not support the resource type will return False - _logger.debug(f"Searching for mapper for resource type: {resource['resourceType']}") - mapper: Optional[DataMapper] = next((mapper for mapper in self._mappers if mapper.can_map(resource["resourceType"])), - None) - - if not mapper: - _logger.warning(f"skipping mapping, unable to find mapper for resource type of {resource['resourceType']}") - continue - - if len(inventory_items := mapper.map(resource)) > 0: - all_inventory.extend(inventory_items) - - _logger.info(f"completed querying AWS config, found {len(all_inventory)} resources") - - # Add the manual items listed as an environment variable - manual_entry_items = json.loads(os.environ["MANUAL_ENTRY_ITEMS"]) - - _logger.info(f"Adding {len(manual_entry_items)} manual entries") - - for item in manual_entry_items: - manual_inventory_item = InventoryData() - for key in item: - setattr(manual_inventory_item, key, item[key]) - all_inventory.append(manual_inventory_item) - - _logger.info(f"completed getting inventory, with a total of {len(all_inventory)}") - - return all_inventory diff --git a/fedramp-integrated-inventory-workbook/deployment/inventory/reports.py b/fedramp-integrated-inventory-workbook/deployment/inventory/reports.py deleted file mode 100644 index 52278472..00000000 --- a/fedramp-integrated-inventory-workbook/deployment/inventory/reports.py +++ /dev/null @@ -1,84 +0,0 @@ -from datetime import datetime -import logging -from pathlib import PurePath -import os, os.path -from typing import List -import boto3 -from openpyxl import load_workbook -from openpyxl.worksheet.worksheet import Worksheet -from openpyxl.utils import get_column_letter -from .mappers import InventoryData - -_logger = logging.getLogger("inventory.reports") -_logger.setLevel(os.environ.get("LOG_LEVEL", logging.INFO)) -_current_dir_name = os.path.dirname(__file__) -_workbook_template_file_name = os.path.join(_current_dir_name, "SSP-A13-FedRAMP-Integrated-Inventory-Workbook-Template.xlsx") -_workbook_output_file_path = PurePath("/tmp/SSP-A13-FedRAMP-Integrated-Inventory.xlsx") -DEFAULT_REPORT_WORKSHEET_FIRST_WRITEABLE_ROW_NUMBER = 6 -DEFAULT_COL_WIDTH = 10 - -class CreateReportCommandHandler(): - def _write_cell_if_value_provided(self, worksheet: Worksheet, column:int, row: int, value: str): - if value: - # Scale the size of the column with the input value if necessary. By default width is None. - if worksheet.column_dimensions[get_column_letter(column)].width is not None: - worksheet.column_dimensions[get_column_letter(column)].width = max(worksheet.column_dimensions[get_column_letter(column)].width, len(value)) - else: - worksheet.column_dimensions[get_column_letter(column)].width = DEFAULT_COL_WIDTH - - worksheet.cell(column=column, row=row, value=value) - - def execute(self, inventory: List[InventoryData]) -> str: - workbook = load_workbook(_workbook_template_file_name) - reportWorksheetName = os.environ.get("REPORT_WORKSHEET_NAME", "Inventory") - reportWorksheet = workbook[reportWorksheetName] - rowNumber: int = int(os.environ.get("REPORT_WORKSHEET_FIRST_WRITEABLE_ROW_NUMBER", DEFAULT_REPORT_WORKSHEET_FIRST_WRITEABLE_ROW_NUMBER)) - _logger.info(f"writing {len(inventory)} rows into worksheet {reportWorksheetName} starting at row {rowNumber}") - - for inventory_row in inventory: - self._write_cell_if_value_provided(reportWorksheet, 1, rowNumber, inventory_row.unique_id) - self._write_cell_if_value_provided(reportWorksheet, 2, rowNumber, inventory_row.ip_address) - self._write_cell_if_value_provided(reportWorksheet, 3, rowNumber, inventory_row.is_virtual) - self._write_cell_if_value_provided(reportWorksheet, 4, rowNumber, inventory_row.is_public) - self._write_cell_if_value_provided(reportWorksheet, 5, rowNumber, inventory_row.dns_name) - self._write_cell_if_value_provided(reportWorksheet, 7, rowNumber, inventory_row.mac_address) - self._write_cell_if_value_provided(reportWorksheet, 8, rowNumber, inventory_row.authenticated_scan_planned) - self._write_cell_if_value_provided(reportWorksheet, 9, rowNumber, inventory_row.baseline_config) - self._write_cell_if_value_provided(reportWorksheet, 12, rowNumber, inventory_row.asset_type) - self._write_cell_if_value_provided(reportWorksheet, 13, rowNumber, inventory_row.hardware_model) - self._write_cell_if_value_provided(reportWorksheet, 14, rowNumber, inventory_row.in_latest_scan) - self._write_cell_if_value_provided(reportWorksheet, 15, rowNumber, inventory_row.software_vendor) - self._write_cell_if_value_provided(reportWorksheet, 16, rowNumber, inventory_row.software_product_name) - self._write_cell_if_value_provided(reportWorksheet, 18, rowNumber, inventory_row.purpose) - self._write_cell_if_value_provided(reportWorksheet, 19, rowNumber, inventory_row.comments) - self._write_cell_if_value_provided(reportWorksheet, 20, rowNumber, inventory_row.asset_tag) - self._write_cell_if_value_provided(reportWorksheet, 21, rowNumber, inventory_row.network_id) - self._write_cell_if_value_provided(reportWorksheet, 22, rowNumber, inventory_row.owner) - self._write_cell_if_value_provided(reportWorksheet, 23, rowNumber, inventory_row.owner) - - rowNumber += 1 - - workbook.save(_workbook_output_file_path) - - _logger.info(f"completed saving inventory into {_workbook_output_file_path}") - - return str(_workbook_output_file_path) - -class DeliverReportCommandHandler(): - def __init__(self, s3_client=boto3.client('s3')): - self._s3_client = s3_client - - def execute(self, report_file_name: str) -> str: - target_path = os.environ["REPORT_TARGET_BUCKET_PATH"] - target_bucket = os.environ["REPORT_TARGET_BUCKET_NAME"] - report_s3_key = os.path.join(target_path, f"{_workbook_output_file_path.stem}-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.xlsx") - - _logger.info(f"uploading file '{report_file_name}' to bucket '{target_bucket}' with key '{report_s3_key}'") - - object_data = open(report_file_name, "rb") - - self._s3_client.put_object(Bucket=target_bucket, Key=report_s3_key, Body=object_data) - - _logger.info(f"completed file upload") - - return f"https://{target_bucket}.s3.amazonaws.com/{report_s3_key}" diff --git a/fedramp-integrated-inventory-workbook/package.sh b/fedramp-integrated-inventory-workbook/package.sh deleted file mode 100755 index a5a4c7f8..00000000 --- a/fedramp-integrated-inventory-workbook/package.sh +++ /dev/null @@ -1,8 +0,0 @@ -rm -fr output -mkdir output -cp -R deployment/inventory output -pip install -r requirements.txt -t output -U --no-deps -chmod -R 755 output -rm -f fedramp-inventory-lambda.zip -(cd output && zip -r8 ../fedramp-inventory-lambda.zip .) -rm -fr output diff --git a/fedramp-integrated-inventory-workbook/requirements.txt b/fedramp-integrated-inventory-workbook/requirements.txt deleted file mode 100644 index f151000b..00000000 --- a/fedramp-integrated-inventory-workbook/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -boto3==1.17.10 -botocore==1.20.10; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' -et-xmlfile==1.0.1 -jdcal==1.4.1 -jmespath==0.10.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' -openpyxl==3.0.6 -python-dateutil==2.8.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -s3transfer==0.3.4 -six==1.15.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -urllib3==1.26.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample.json deleted file mode 100644 index 0b87ceee..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample.json +++ /dev/null @@ -1,294 +0,0 @@ -[ - { - "configuration": { - "allocatedStorage": 1.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora5.6", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1b", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.6.10a", - "masterUsername": "admin", - "dBInstanceClass": "db.t2.small", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 1.0, - "promotionTier": 1.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-5-6", - "status": "in-sync" - } - ], - "kmsKeyId": "arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/c123f12f-12c1-1234-af01-1e1afdafcc12", - "dBClusterIdentifier": "mydbinstance-priv-cluster", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:42-22:12", - "dbiResourceId": "db-afdasfd234fasdfasdf", - "deletionProtection": false, - "dBInstanceIdentifier": "mydbinstance-priv-us-gov-east-1b", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "engine": "aurora", - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": true, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-f714e69f", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "fri:22:45-fri:23:15", - "storageType": "aurora", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 20.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.sqlserver-ex-14.0", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1c", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "14.00.3223.3.v1", - "masterUsername": "amin", - "dBInstanceClass": "db.t2.micro", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 0.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:sqlserver-ex-14-00", - "status": "in-sync" - } - ], - "dbInstancePort": 0.0, - "preferredBackupWindow": "17:30-18:00", - "dbiResourceId": "db-asdfa234rfasdfasdfasf", - "deletionProtection": false, - "dBInstanceIdentifier": "mysqldbinstance-priv", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-priv", - "engine": "sqlserver-ex", - "maxAllocatedStorage": 1000.0, - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "characterSetName": "SQL_Latin1_General_CP1_CI_AS", - "domainMemberships": [], - "storageEncrypted": false, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-0d598ad8e2bf21967", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [], - "masterUserPassword": "****" - }, - "licenseModel": "license-included", - "preferredMaintenanceWindow": "thu:22:00-thu:22:30", - "storageType": "gp2", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-priv", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 1.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora5.6", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1a", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.6.10a", - "masterUsername": "admin", - "instanceCreateTime": "2019-11-20T21:54:16.205Z", - "dBInstanceClass": "db.t2.small", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "available", - "backupRetentionPeriod": 1.0, - "promotionTier": 1.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-5-6", - "status": "in-sync" - } - ], - "kmsKeyId": "arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/c123f12f-12c1-1234-af01-1e1afdafcc12", - "dBClusterIdentifier": "mydbinstance-priv-cluster", - "cACertificateIdentifier": "rds-ca-2017", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:42-22:12", - "dbiResourceId": "db-asdf234rfasdfasdfasdfas", - "deletionProtection": false, - "dBInstanceIdentifier": "mydbinstance-priv", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv", - "endpoint": { - "hostedZoneId": "Z1OCXXRO09ICTP", - "address": "mydbinstance-priv.23fasdf23dfads.us-gov-east-1.rds.amazonaws.com", - "port": 3306.0 - }, - "engine": "aurora", - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": true, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-f714e69f", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "tue:21:53-tue:22:23", - "storageType": "aurora", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv", - "tags": [ - { - "tag": "workload-type\\u003dother", - "value": "other", - "key": "workload-type" - } - ], - "resourceType": "AWS::RDS::DBInstance" - }, -] \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample2.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample2.json deleted file mode 100644 index 4de2cbc0..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample2.json +++ /dev/null @@ -1,492 +0,0 @@ -[ - { - "configuration": { - "allocatedStorage": 1.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora5.6", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1b", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.6.10a", - "masterUsername": "admin", - "dBInstanceClass": "db.t2.small", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 1.0, - "promotionTier": 1.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-5-6", - "status": "in-sync" - } - ], - "kmsKeyId": "arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/c123f12f-12c1-1234-af01-1e1afdafcc12", - "dBClusterIdentifier": "mydbinstance-priv-cluster", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:42-22:12", - "dbiResourceId": "db-dfassadf23fdfas", - "deletionProtection": false, - "dBInstanceIdentifier": "mydbinstance-priv-us-gov-east-1b", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "engine": "aurora", - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": true, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-f714e69f", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "fri:22:45-fri:23:15", - "storageType": "aurora", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 20.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.mysql5.7", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1c", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.7.24", - "masterUsername": "admin", - "instanceCreateTime": "2019-11-20T21:59:38.781Z", - "dBInstanceClass": "db.t2.micro", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "available", - "backupRetentionPeriod": 0.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:mysql-5-7", - "status": "in-sync" - } - ], - "cACertificateIdentifier": "rds-ca-2017", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:06-21:36", - "dbiResourceId": "db-32dfasdfsf23e4234", - "deletionProtection": false, - "dBInstanceIdentifier": "mymysqldbinstance-pub", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mymysqldbinstance-pub", - "endpoint": { - "hostedZoneId": "Z1OCXXRO09ICTP", - "address": "mymysqldbinstance-pub.32r1adsfasdfa.us-gov-east-1.rds.amazonaws.com", - "port": 3306.0 - }, - "engine": "mysql", - "maxAllocatedStorage": 1000.0, - "publiclyAccessible": true, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "dBName": "mydbname", - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": false, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-015d198dd14f4f1ed", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "sat:22:16-sat:22:46", - "storageType": "gp2", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": true - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mymysqldbinstance-pub", - "tags": [ - { - "tag": "workload-type\\u003dother", - "value": "other", - "key": "workload-type" - } - ], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 20.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.sqlserver-ex-14.0", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1c", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "14.00.3223.3.v1", - "masterUsername": "amin", - "dBInstanceClass": "db.t2.micro", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 0.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:sqlserver-ex-14-00", - "status": "in-sync" - } - ], - "dbInstancePort": 0.0, - "preferredBackupWindow": "17:30-18:00", - "dbiResourceId": "db-fasdfa234234dafasfas", - "deletionProtection": false, - "dBInstanceIdentifier": "mysqldbinstance-priv", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-priv", - "engine": "sqlserver-ex", - "maxAllocatedStorage": 1000.0, - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "characterSetName": "SQL_Latin1_General_CP1_CI_AS", - "domainMemberships": [], - "storageEncrypted": false, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-0d598ad8e2bf21967", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [], - "masterUserPassword": "****" - }, - "licenseModel": "license-included", - "preferredMaintenanceWindow": "thu:22:00-thu:22:30", - "storageType": "gp2", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-priv", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 1.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora5.6", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1a", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.6.10a", - "masterUsername": "admin", - "instanceCreateTime": "2019-11-20T21:54:16.205Z", - "dBInstanceClass": "db.t2.small", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "available", - "backupRetentionPeriod": 1.0, - "promotionTier": 1.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-5-6", - "status": "in-sync" - } - ], - "kmsKeyId": "arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/c123f12f-12c1-1234-af01-1e1afdafcc12", - "dBClusterIdentifier": "mydbinstance-priv-cluster", - "cACertificateIdentifier": "rds-ca-2017", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:42-22:12", - "dbiResourceId": "db-dfasf23rasdfasdfasdf", - "deletionProtection": false, - "dBInstanceIdentifier": "mydbinstance-priv", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv", - "endpoint": { - "hostedZoneId": "Z1OCXXRO09ICTP", - "address": "mydbinstance-priv.ckt8fknykow5.us-gov-east-1.rds.amazonaws.com", - "port": 3306.0 - }, - "engine": "aurora", - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": true, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-f714e69f", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "tue:21:53-tue:22:23", - "storageType": "aurora", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv", - "tags": [ - { - "tag": "workload-type\\u003dother", - "value": "other", - "key": "workload-type" - } - ], - "resourceType": "AWS::RDS::DBInstance" - }, - { - "configuration": { - "allocatedStorage": 20.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.sqlserver-ex-14.0", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1b", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "14.00.3223.3.v1", - "masterUsername": "admin", - "dBInstanceClass": "db.t2.micro", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 0.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:sqlserver-ex-14-00", - "status": "in-sync" - } - ], - "dbInstancePort": 0.0, - "preferredBackupWindow": "23:29-23:59", - "dbiResourceId": "db-fasdf23r4fasdfasfasdf", - "deletionProtection": false, - "dBInstanceIdentifier": "mysqldbinstance-pub", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-pub", - "engine": "sqlserver-ex", - "maxAllocatedStorage": 1000.0, - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "characterSetName": "SQL_Latin1_General_CP1_CI_AS", - "domainMemberships": [], - "storageEncrypted": false, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-04f5ba8ece99c42e0", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [], - "masterUserPassword": "****" - }, - "licenseModel": "license-included", - "preferredMaintenanceWindow": "sat:20:56-sat:21:26", - "storageType": "gp2", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": true - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mysqldbinstance-pub", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" - } -] \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_classic_elb.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_classic_elb.json deleted file mode 100644 index 996f8e7e..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_classic_elb.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "resourceName": "sampleClassicELB", - "arn": "arn:aws-us-gov:elasticloadbalancing:us-gov-east-1:123456789012:loadbalancer/my-test-loadbalancer", - "configuration": { - "canonicalHostedZoneNameID": "Z166TLBEWOO7G0", - "scheme": "internet-facing", - "policies": { - "lbcookieStickinessPolicies": [], - "appCookieStickinessPolicies": [], - "otherPolicies": [] - }, - "availabilityZones": [ - { - "value": "us-gov-east-1b" - }, - { - "value": "us-gov-east-1a" - }, - { - "value": "us-gov-east-1c" - } - ], - "backendServerDescriptions": [], - "sourceSecurityGroup": { - "ownerAlias": "123456789012", - "groupName": "default" - }, - "loadBalancerName": "my-test-loadbalancer", - "listenerDescriptions": [ - { - "listener": { - "instancePort": 80, - "instanceProtocol": "HTTP", - "protocol": "HTTP", - "loadBalancerPort": 80 - }, - "policyNames": [] - } - ], - "vpcid": "vpc-88e50ee1", - "dnsname": "my-test-loadbalancer-123415.us-gov-east-1.elb.amazonaws.com", - "createdTime": 1574121317790, - "subnets": [ - { - "value": "subnet-83cf26ea" - }, - { - "value": "subnet-917b51db" - }, - { - "value": "subnet-d95a51a1" - } - ], - "securityGroups": [ - { - "value": "sg-f714e69f" - } - ], - "canonicalHostedZoneName": "my-test-loadbalancer-1795943216.us-gov-east-1.elb.amazonaws.com" - }, - "tags": [ - { - "tag": "MyLB=IsClassic", - "value": "IsClassic", - "key": "MyLB" - } - ], - "resourceType": "AWS::ElasticLoadBalancing::LoadBalancer" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_dynamo_table.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_dynamo_table.json deleted file mode 100644 index dfc86292..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_dynamo_table.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "resourceName": "sampleDynamoDB", - "configuration": { - "attributeDefinitions": [ - { - "attributeType": "S", - "attributeName": "record-type" - } - ], - "keySchema": [ - { - "attributeName": "record-type", - "keyType": "HASH" - } - ], - "tableId": "c123f12f-12c1-1234-af01-1e1afdafcc12", - "provisionedThroughput": { - "numberOfDecreasesToday": 0.0, - "writeCapacityUnits": 5.0, - "readCapacityUnits": 5.0 - }, - "tableStatus": "ACTIVE", - "tableName": "my-temp-table", - "creationDateTime": "2019-11-20T16:36:54.455Z", - "tableArn": "arn:aws-us-gov:dynamodb:us-gov-east-1:123456789012:table/my-temp-table" - }, - "arn": "arn:aws-us-gov:dynamodb:us-gov-east-1:123456789012:table/my-temp-table", - "tags": [ - { - "tag": "mytag\\u003dvalue", - "value": "value", - "key": "mytag" - } - ], - "resourceType": "AWS::DynamoDB::Table" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_ec2.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_ec2.json deleted file mode 100644 index dc0213ac..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_ec2.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "resourceName": "sampleEC2", - "configuration": { - "subnetId": "subnet-83cf26ea", - "virtualizationType": "hvm", - "capacityReservationSpecification": { - "capacityReservationPreference": "open" - }, - "amiLaunchIndex": 0, - "enaSupport": true, - "elasticInferenceAcceleratorAssociations": [], - "sourceDestCheck": true, - "hibernationOptions": { - "configured": false - }, - "instanceId": "i-03cbdcb2bb8425d47", - "vpcId": "vpc-88e50ee1", - "hypervisor": "xen", - "rootDeviceName": "/dev/xvda", - "productCodes": [], - "state": { - "code": "16", - "name": "running" - }, - "architecture": "x86_64", - "ebsOptimized": true, - "imageId": "ami-6e92711f", - "blockDeviceMappings": [ - { - "ebs": { - "volumeId": "vol-083ba735bb011e5c8", - "deleteOnTermination": true, - "attachTime": "2019-11-19T16:20:24.000Z", - "status": "attached" - }, - "deviceName": "/dev/xvda" - } - ], - "publicIpAddress": "11.111.111.111", - "stateTransitionReason": "", - "clientToken": "", - "instanceType": "t3.micro", - "cpuOptions": { - "threadsPerCore": 2, - "coreCount": 1 - }, - "monitoring": { - "state": "disabled" - }, - "publicDnsName": "ec2-11-111-111-111.us-gov-east-1.compute.amazonaws.com", - "iamInstanceProfile": { - "id": "AIPAXGYJARCHP3CJJFWOF", - "arn": "arn:aws-us-gov:iam::123456789012:instance-profile/SsmManagedInstance" - }, - "privateIpAddress": "172.31.0.188", - "rootDeviceType": "ebs", - "tags": [ - { - "value": "True", - "key": "IsIsolated" - } - ], - "launchTime": "2019-11-19T16:20:24.000Z", - "elasticGpuAssociations": [], - "licenses": [], - "networkInterfaces": [ - { - "networkInterfaceId": "eni-09ca92d35e333fa1b", - "privateIpAddresses": [ - { - "association": { - "ipOwnerId": "amazon", - "publicIp": "11.111.111.111", - "publicDnsName": "ec2-11-111-111-111.us-gov-east-1.compute.amazonaws.com" - }, - "privateDnsName": "ip-172-31-0-188.us-gov-east-1.compute.internal", - "privateIpAddress": "172.31.0.188", - "primary": true - } - ], - "subnetId": "subnet-83cf26ea", - "association": { - "ipOwnerId": "amazon", - "publicIp": "11.111.111.111", - "publicDnsName": "ec2-11-111-111-111.us-gov-east-1.compute.amazonaws.com" - }, - "description": "", - "groups": [ - { - "groupName": "default", - "groupId": "sg-f714e69f" - } - ], - "ipv6Addresses": [], - "ownerId": "123456789012", - "sourceDestCheck": true, - "privateIpAddress": "172.31.0.188", - "interfaceType": "interface", - "macAddress": "06:d1:af:2f:a4:46", - "attachment": { - "attachmentId": "eni-attach-015730ee59e442f05", - "deleteOnTermination": true, - "deviceIndex": 0, - "attachTime": "2019-11-19T16:20:24.000Z", - "status": "attached" - }, - "vpcId": "vpc-88e50ee1", - "privateDnsName": "ip-172-31-0-188.us-gov-east-1.compute.internal", - "status": "in-use" - } - ], - "privateDnsName": "ip-172-31-0-188.us-gov-east-1.compute.internal", - "securityGroups": [ - { - "groupName": "default", - "groupId": "sg-f714e69f" - } - ], - "placement": { - "groupName": "", - "tenancy": "default", - "availabilityZone": "us-gov-east-1a" - } - }, - "tags": [ - { - "tag": "IsIsolated=True", - "value": "True", - "key": "IsIsolated" - } - ], - "resourceType": "AWS::EC2::Instance" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_es.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_es.json deleted file mode 100644 index 03b72a0e..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_es.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-07-08T20:42:22.993Z", - "configurationItemStatus": "OK", - "configurationStateId": "0987654321", - "configurationItemMD5Hash": "", - "arn": "arn:aws:es:us-west-2:123456789:domain/my-elasticsearch", - "resourceType": "AWS::Elasticsearch::Domain", - "resourceId": "123456789/my-elasticsearch", - "resourceName": "my-elasticsearch", - "awsRegion": "us-west-2", - "availabilityZone": "Multiple Availability Zones", - "tags": { - "Environment": "custom" - }, - "relatedEvents": [], - "relationships": [], - "configuration": { - "domainId": "123456789/my-elasticsearch", - "domainName": "my-elasticsearch", - "created": true, - "deleted": false, - "endpoints": { - "vpc": "vpc-my-elasticsearch-vpcid.us-west-2.es.amazonaws.com" - }, - "processing": false, - "upgradeProcessing": false, - "elasticsearchVersion": "7.10", - "elasticsearchClusterConfig": { - "instanceType": "t3.medium.elasticsearch", - "instanceCount": 1, - "dedicatedMasterEnabled": false, - "zoneAwarenessEnabled": false, - "warmEnabled": false, - "coldStorageOptions": { - "enabled": false - } - }, - "accessPolicies": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":\"*\",\"Resource\":\"*\"}]}", - "snapshotOptions": { - "automatedSnapshotStartHour": 0 - }, - "cognitoOptions": { - "enabled": false - }, - "encryptionAtRestOptions": { - "enabled": false - }, - "nodeToNodeEncryptionOptions": { - "enabled": true - }, - "advancedOptions": { - "rest.action.multi.allow_explicit_index": "true" - }, - "logPublishingOptions": { - "ES_APPLICATION_LOGS": { - "cloudWatchLogsLogGroupArn": "arn:aws:logs:us-west-2:123456789:log-group:/logs:*", - "enabled": true - } - }, - "serviceSoftwareOptions": { - "currentVersion": "R20210426", - "newVersion": "R20210426-P2", - "updateAvailable": true, - "cancellable": false, - "updateStatus": "ELIGIBLE", - "description": "A new software release R20210426-P2 is available. This release will be automatically deployed if no action is taken.", - "automatedUpdateDate": 1625731585000, - "optionalDeployment": false - }, - "domainEndpointOptions": { - "enforceHTTPS": false, - "customEndpointEnabled": false, - "tlssecurityPolicy": "Policy-Min-TLS-1-0-2019-07" - }, - "advancedSecurityOptions": { - "enabled": false, - "internalUserDatabaseEnabled": false - }, - "autoTuneOptions": { - "state": "ENABLE_IN_PROGRESS" - }, - "arn": "arn:aws:es:us-west-2:123456789:domain/my-elasticsearch", - "ebsoptions": { - "volumeType": "gp2", - "volumeSize": 50, - "ebsenabled": true - }, - "vpcoptions": { - "subnetIds": [ - "subnet-abcdefghijklmnop" - ], - "availabilityZones": [ - "us-west-2b" - ], - "securityGroupIds": [ - "sg-abcdefghijklmnop" - ], - "vpcid": "vpc-abcdefghijklmnop" - } - }, - "supplementaryConfiguration": { - "Tags": [ - ] - }, - "resourceTransitionStatus": "None" -} diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_lambda_function.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_lambda_function.json deleted file mode 100644 index ba56694d..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_lambda_function.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-02-18T23:22:10.603Z", - "configurationItemStatus": "ResourceDiscovered", - "configurationStateId": "12345", - "configurationItemMD5Hash": "", - "arn": "arn:aws:lambda:us-west-2:123456789:function:InventoryCollector", - "resourceType": "AWS::Lambda::Function", - "resourceId": "InventoryCollector", - "resourceName": "InventoryCollector", - "awsRegion": "us-west-2", - "availabilityZone": "Not Applicable", - "tags": { - "aws:cloudformation:stack-name": "fedramp-stack", - "aws:cloudformation:stack-id": "arn:aws:cloudformation:us-west-2:123456789:stack/fedramp-stack/62db7360-723e-11eb-a7ab-06b1b80a4e11", - "aws:cloudformation:logical-id": "InventoryCollectorLambda" - }, - "relatedEvents": [], - "relationships": [ - { - "resourceType": "AWS::IAM::Role", - "resourceName": "InventoryCollectorLambdaExecuteRole", - "relationshipName": "Is associated with " - } - ], - "configuration": { - "functionName": "InventoryCollector", - "functionArn": "arn:aws:lambda:us-west-2:123456789:function:InventoryCollector", - "runtime": "python3.8", - "role": "arn:aws:iam::123456789:role/InventoryCollectorLambdaExecuteRole", - "handler": "inventory.handler.lambda_handler", - "codeSize": 9542306, - "description": "", - "timeout": 900, - "memorySize": 128, - "lastModified": "2021-02-18T23:11:18.936+0000", - "codeSha256": "9USjof7eQeundTr3NmcXcIq/8XaGk+kAzJKib0mHRbs=", - "version": "$LATEST", - "tracingConfig": { - "mode": "PassThrough" - }, - "revisionId": "a73f18f8-55d1-4e22-a277-f7905be039b4", - "layers": [], - "state": "Active", - "lastUpdateStatus": "Successful", - "fileSystemConfigs": [], - "packageType": "Zip" - }, - "supplementaryConfiguration": { - "Policy": "{\"Version\":\"2012-10-17\",\"Id\":\"default\",\"Statement\":[{\"Sid\":\"fedramp-stack-PermissionForEventsToInvokeLambda-Y17BYAE61UZ6\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"events.amazonaws.com\"},\"Action\":\"lambda:InvokeFunction\",\"Resource\":\"arn:aws:lambda:us-west-2:123456789:function:InventoryCollector\",\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:events:us-west-2:123456789:rule/InventoryCollector-ScheduleExpression\"}}}]}", - "Tags": { - "aws:cloudformation:stack-name": "fedramp-stack", - "aws:cloudformation:stack-id": "arn:aws:cloudformation:us-west-2:123456789:stack/fedramp-stack/62db7360-723e-11eb-a7ab-06b1b80a4e11", - "aws:cloudformation:logical-id": "InventoryCollectorLambda" - } - }, - "resourceTransitionStatus": "None" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_db.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_db.json deleted file mode 100644 index 5c144de5..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_db.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "resourceName": "samplevrdsDB", - "configuration": { - "allocatedStorage": 1.0, - "enabledCloudwatchLogsExports": [], - "readReplicaDBClusterIdentifiers": [], - "associatedRoles": [], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora5.6", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-gov-east-1b", - "dBSecurityGroups": [], - "statusInfos": [], - "engineVersion": "5.6.10a", - "masterUsername": "admin", - "dBInstanceClass": "db.t2.small", - "processorFeatures": [], - "readReplicaDBInstanceIdentifiers": [], - "monitoringInterval": 0.0, - "dBInstanceStatus": "creating", - "backupRetentionPeriod": 1.0, - "promotionTier": 1.0, - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-5-6", - "status": "in-sync" - } - ], - "kmsKeyId": "arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/c123f12f-12c1-1234-af01-1e1afdafcc12", - "dBClusterIdentifier": "mydbinstance-priv-cluster", - "dbInstancePort": 0.0, - "preferredBackupWindow": "21:42-22:12", - "dbiResourceId": "db-fasfda23r4fasdfasdfasdf", - "deletionProtection": false, - "dBInstanceIdentifier": "mydbinstance-priv-us-gov-east-1b", - "dBInstanceArn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "engine": "aurora", - "publiclyAccessible": false, - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": false, - "multiAZ": false, - "domainMemberships": [], - "storageEncrypted": true, - "dBSubnetGroup": { - "vpcId": "vpc-88e50ee1", - "subnets": [ - { - "subnetIdentifier": "subnet-d95a51a1", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1b" - } - }, - { - "subnetIdentifier": "subnet-83cf26ea", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1a" - } - }, - { - "subnetIdentifier": "subnet-917b51db", - "subnetStatus": "Active", - "subnetAvailabilityZone": { - "name": "us-gov-east-1c" - } - } - ], - "subnetGroupStatus": "Complete", - "dBSubnetGroupName": "default", - "dBSubnetGroupDescription": "default" - }, - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-f714e69f", - "status": "active" - } - ], - "pendingModifiedValues": { - "processorFeatures": [] - }, - "licenseModel": "general-public-license", - "preferredMaintenanceWindow": "fri:22:45-fri:23:15", - "storageType": "aurora", - "autoMinorVersionUpgrade": true, - "copyTagsToSnapshot": false - }, - "arn": "arn:aws-us-gov:rds:us-gov-east-1:123456789012:db:mydbinstance-priv-us-gov-east-1b", - "tags": [], - "resourceType": "AWS::RDS::DBInstance" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_instance.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_instance.json deleted file mode 100644 index adf355ab..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_rds_instance.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-02-23T16:22:06.705Z", - "configurationItemStatus": "ResourceDiscovered", - "configurationStateId": "123456789", - "configurationItemMD5Hash": "", - "arn": "arn:aws:rds:us-west-2:123456789:db:database-1-instance-1", - "resourceType": "AWS::RDS::DBInstance", - "resourceId": "db-ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "resourceName": "database-1-instance-1", - "awsRegion": "us-west-2", - "availabilityZone": "us-west-2a", - "resourceCreationTime": "2021-02-23T16:17:07.900Z", - "tags": {}, - "relatedEvents": [], - "relationships": [ - { - "resourceType": "AWS::RDS::DBSubnetGroup", - "resourceId": "default-vpc-123456789", - "relationshipName": "Is associated with DBSubnetGroup" - }, - { - "resourceType": "AWS::EC2::SecurityGroup", - "resourceId": "sg-123456789", - "relationshipName": "Is associated with SecurityGroup" - } - ], - "configuration": { - "dBInstanceIdentifier": "database-1-instance-1", - "dBInstanceClass": "db.r5.large", - "engine": "aurora-mysql", - "dBInstanceStatus": "available", - "masterUsername": "admin", - "endpoint": { - "address": "database-1-instance-1.abcdefg.us-west-2.rds.amazonaws.com", - "port": 3306, - "hostedZoneId": "ASDJSJKSDNBAJKSD" - }, - "allocatedStorage": 1, - "instanceCreateTime": "2021-02-23T16:17:07.900Z", - "preferredBackupWindow": "11:22-11:52", - "backupRetentionPeriod": 1, - "dBSecurityGroups": [], - "vpcSecurityGroups": [ - { - "vpcSecurityGroupId": "sg-123456789", - "status": "active" - } - ], - "dBParameterGroups": [ - { - "dBParameterGroupName": "default.aurora-mysql5.7", - "parameterApplyStatus": "in-sync" - } - ], - "availabilityZone": "us-west-2a", - "dBSubnetGroup": { - "dBSubnetGroupName": "default-vpc-123456789", - "dBSubnetGroupDescription": "Created from the RDS Management Console", - "vpcId": "vpc-123456789", - "subnetGroupStatus": "Complete", - "subnets": [ - { - "subnetIdentifier": "subnet-123456789", - "subnetAvailabilityZone": { - "name": "us-west-2c" - }, - "subnetOutpost": {}, - "subnetStatus": "Active" - }, - { - "subnetIdentifier": "subnet-123456789", - "subnetAvailabilityZone": { - "name": "us-west-2a" - }, - "subnetOutpost": {}, - "subnetStatus": "Active" - }, - { - "subnetIdentifier": "subnet-123456789", - "subnetAvailabilityZone": { - "name": "us-west-2b" - }, - "subnetOutpost": {}, - "subnetStatus": "Active" - }, - { - "subnetIdentifier": "subnet-123456789", - "subnetAvailabilityZone": { - "name": "us-west-2d" - }, - "subnetOutpost": {}, - "subnetStatus": "Active" - } - ] - }, - "preferredMaintenanceWindow": "tue:07:47-tue:08:17", - "pendingModifiedValues": { - "processorFeatures": [] - }, - "multiAZ": false, - "engineVersion": "5.7.mysql_aurora.2.07.2", - "autoMinorVersionUpgrade": true, - "readReplicaDBInstanceIdentifiers": [], - "readReplicaDBClusterIdentifiers": [], - "licenseModel": "general-public-license", - "optionGroupMemberships": [ - { - "optionGroupName": "default:aurora-mysql-5-7", - "status": "in-sync" - } - ], - "publiclyAccessible": false, - "statusInfos": [], - "storageType": "aurora", - "dbInstancePort": 0, - "dBClusterIdentifier": "database-1", - "storageEncrypted": true, - "kmsKeyId": "arn:aws:kms:us-west-2:123456789:key/abc-def-ghi-jkl", - "dbiResourceId": "db-123456789", - "cACertificateIdentifier": "rds-ca-123456789", - "domainMemberships": [], - "copyTagsToSnapshot": false, - "monitoringInterval": 60, - "enhancedMonitoringResourceArn": "arn:aws:logs:us-west-2:123456789:log-group:RDSOSMetrics:log-stream:db-123456789", - "monitoringRoleArn": "arn:aws:iam::123456789:role/rds-monitoring-role", - "promotionTier": 1, - "dBInstanceArn": "arn:aws:rds:us-west-2:123456789:db:database-1-instance-1", - "iAMDatabaseAuthenticationEnabled": false, - "performanceInsightsEnabled": true, - "performanceInsightsKMSKeyId": "arn:aws:kms:us-west-2:123456789:key/abc-def-ghi-jkl", - "performanceInsightsRetentionPeriod": 7, - "enabledCloudwatchLogsExports": [], - "processorFeatures": [], - "deletionProtection": false, - "associatedRoles": [], - "tagList": [] - }, - "supplementaryConfiguration": { - "Tags": [] - }, - "resourceTransitionStatus": "None" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_route_table.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_route_table.json deleted file mode 100644 index 40175b80..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_route_table.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-02-19T16:13:14.033Z", - "configurationItemStatus": "OK", - "configurationStateId": "123456789", - "configurationItemMD5Hash": "", - "arn": "arn:aws:ec2:us-west-2:123456789:route-table/rtb-123456789", - "resourceType": "AWS::EC2::RouteTable", - "resourceId": "rtb-123456789", - "awsRegion": "us-west-2", - "availabilityZone": "Not Applicable", - "tags": {}, - "relatedEvents": [], - "relationships": [ - { - "resourceType": "AWS::EC2::VPC", - "resourceId": "vpc-12345678", - "relationshipName": "Is contained in Vpc" - }, - { - "resourceType": "AWS::EC2::Subnet", - "resourceId": "subnet-12345678", - "relationshipName": "Contains Subnet" - } - ], - "configuration": { - "associations": [ - { - "main": false, - "routeTableAssociationId": "rtbassoc-123456789", - "routeTableId": "rtb-123456789", - "subnetId": "subnet-123456789", - "associationState": { - "state": "associated" - } - }, - { - "main": true, - "routeTableAssociationId": "rtbassoc-12345678", - "routeTableId": "rtb-123456789", - "associationState": { - "state": "associated" - } - } - ], - "propagatingVgws": [], - "routeTableId": "rtb-123456789", - "routes": [ - { - "destinationCidrBlock": "111.11.11.11/11", - "gatewayId": "local", - "origin": "CreateRouteTable", - "state": "active" - }, - { - "destinationCidrBlock": "0.0.0.0/0", - "gatewayId": "igw-123456789", - "origin": "CreateRoute", - "state": "active" - } - ], - "tags": [], - "vpcId": "vpc-12345678", - "ownerId": "123456789" - }, - "supplementaryConfiguration": {}, - "resourceTransitionStatus": "None" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_s3.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_s3.json deleted file mode 100644 index d81fc750..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_s3.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-02-16T19:42:38.705Z", - "configurationItemStatus": "ResourceDiscovered", - "configurationStateId": "123456789", - "configurationItemMD5Hash": "", - "arn": "arn:aws:s3:::fedramp-bucket", - "resourceType": "AWS::S3::Bucket", - "resourceId": "fedramp-bucket", - "resourceName": "fedramp-bucket", - "awsRegion": "us-west-2", - "availabilityZone": "Regional", - "resourceCreationTime": "2021-02-16T19:29:10.000Z", - "tags": {}, - "relatedEvents": [], - "relationships": [], - "configuration": { - "name": "fedramp-bucket", - "owner": { - "id": "abcdefghijklmnopqrstuvwxyz" - }, - "creationDate": "2021-02-16T19:29:10.000Z" - }, - "supplementaryConfiguration": { - "AccessControlList": "{\"grantSet\":null,\"grantList\":[{\"grantee\":{\"id\":\"abcdefghijklmnopqrstuvwxyz2\",\"displayName\":null},\"permission\":\"FullControl\"}],\"owner\":{\"displayName\":null,\"id\":\"abcdefghijklmnopqrstuvwxyz3\"},\"isRequesterCharged\":false}", - "PublicAccessBlockConfiguration": { - "blockPublicAcls": true, - "ignorePublicAcls": true, - "blockPublicPolicy": true, - "restrictPublicBuckets": true - }, - "BucketLoggingConfiguration": { - "destinationBucketName": null, - "logFilePrefix": null - }, - "BucketPolicy": { - "policyText": null - }, - "BucketAccelerateConfiguration": { - "status": null - }, - "IsRequesterPaysEnabled": false, - "BucketVersioningConfiguration": { - "status": "Off", - "isMfaDeleteEnabled": null - }, - "BucketNotificationConfiguration": { - "configurations": {} - } - }, - "resourceTransitionStatus": "None" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_v2elb.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_v2elb.json deleted file mode 100644 index d1bda9ab..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_v2elb.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "resourceName": "samplev2elb", - "configuration": { - "loadBalancerArn": "arn:aws-us-gov:elasticloadbalancing:us-gov-east-1:123456789012:loadbalancer/net/nlb/f9ad7b767f625655", - "scheme": "internet-facing", - "loadBalancerName": "nlb", - "canonicalHostedZoneId": "Z1ZSMQQ6Q24QQ8", - "vpcId": "vpc-12341234", - "createdTime": "2019-11-20T11:23:16.516Z", - "availabilityZones": [ - { - "subnetId": "subnet-83cf26ea", - "zoneName": "us-gov-east-1a", - "loadBalancerAddresses": [ - {} - ] - }, - { - "subnetId": "subnet-d95a51a1", - "zoneName": "us-gov-east-1b", - "loadBalancerAddresses": [ - { "ipAddress": "11.111.111.111"} - ] - } - ], - "securityGroups": [], - "dNSName": "nlb-f9ad7b767f625655.elb.us-gov-east-1.amazonaws.com", - "state": { - "code": "active" - }, - "type": "network", - "ipAddressType": "ipv4" - }, - "arn": "arn:aws-us-gov:elasticloadbalancing:us-gov-east-1:123456789012:loadbalancer/net/nlb/f9ad7b767f625655", - "tags": [], - "resourceType": "AWS::ElasticLoadBalancingV2::LoadBalancer" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_vpc.json b/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_vpc.json deleted file mode 100644 index e3a47ac3..00000000 --- a/fedramp-integrated-inventory-workbook/tests/sample_config_query_results/sample_vpc.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "version": "1.3", - "accountId": "123456789", - "configurationItemCaptureTime": "2021-02-17T22:13:00.257Z", - "configurationItemStatus": "OK", - "configurationStateId": "123456", - "configurationItemMD5Hash": "", - "arn": "arn:aws:ec2:us-west-2:123456789:vpc/vpc-12345", - "resourceType": "AWS::EC2::VPC", - "resourceId": "vpc-12345", - "awsRegion": "us-west-2", - "availabilityZone": "Multiple Availability Zones", - "tags": {}, - "relatedEvents": [], - "relationships": [ - { - "resourceType": "AWS::EC2::Instance", - "resourceId": "i-12345", - "relationshipName": "Contains Instance" - }, - { - "resourceType": "AWS::EC2::RouteTable", - "resourceId": "rtb-12345", - "relationshipName": "Contains RouteTable" - }, - { - "resourceType": "AWS::EC2::Subnet", - "resourceId": "subnet-12345", - "relationshipName": "Contains Subnet" - }, - { - "resourceType": "AWS::EC2::Subnet", - "resourceId": "subnet-12345", - "relationshipName": "Contains Subnet" - }, - { - "resourceType": "AWS::EC2::Subnet", - "resourceId": "subnet-12345", - "relationshipName": "Contains Subnet" - }, - { - "resourceType": "AWS::EC2::NetworkAcl", - "resourceId": "acl-12345", - "relationshipName": "Contains NetworkAcl" - }, - { - "resourceType": "AWS::EC2::InternetGateway", - "resourceId": "igw-12345", - "relationshipName": "Is attached to InternetGateway" - }, - { - "resourceType": "AWS::EC2::NetworkInterface", - "resourceId": "eni-1234", - "relationshipName": "Contains NetworkInterface" - }, - { - "resourceType": "AWS::EC2::Subnet", - "resourceId": "subnet-1234", - "relationshipName": "Contains Subnet" - }, - { - "resourceType": "AWS::EC2::SecurityGroup", - "resourceId": "sg-1234", - "relationshipName": "Contains SecurityGroup" - } - ], - "configuration": { - "cidrBlock": "111.11.11.11", - "dhcpOptionsId": "dopt-1234", - "state": "available", - "vpcId": "vpc-12345", - "ownerId": "123456789", - "instanceTenancy": "default", - "ipv6CidrBlockAssociationSet": [], - "cidrBlockAssociationSet": [ - { - "associationId": "vpc-cidr-assoc-12345", - "cidrBlock": "111.11.11.11", - "cidrBlockState": { - "state": "associated" - } - } - ], - "isDefault": true, - "tags": [] - }, - "supplementaryConfiguration": {}, - "resourceTransitionStatus": "None" -} \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/test_dynamo_table_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_dynamo_table_mapper.py deleted file mode 100644 index 5c7f8a13..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_dynamo_table_mapper.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -import json -import os -import pytest -from inventory.mappers import DynamoDbTableDataMapper - -@pytest.fixture() -def full_dynamo_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_dynamo_table.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_dynamo_table_then_base_attributes_mapped(full_dynamo_config): - mapper = DynamoDbTableDataMapper() - - mapped_result = mapper.map(full_dynamo_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].unique_id == full_dynamo_config["arn"], "ARN should be mapped to unique id" diff --git a/fedramp-integrated-inventory-workbook/tests/test_ec2_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_ec2_mapper.py deleted file mode 100644 index d67a4816..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_ec2_mapper.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -import json -import os -import pytest -from inventory.mappers import EC2DataMapper - -@pytest.fixture() -def full_ec2_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_ec2.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_resource_type_is_not_ec2_then_empty_array_is_returned(full_ec2_config): - full_ec2_config["resourceType"] = "NOT EC2" - - mapper = EC2DataMapper() - - assert mapper.map(full_ec2_config) == [] - -def test_given_isntance_has_no_public_dns_ec2_then_dns_private_dns_is_used(full_ec2_config): - del(full_ec2_config["configuration"]["publicDnsName"]) - del(full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["association"]) - - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row was expected" - assert mapped_result[0].dns_name == full_ec2_config["configuration"]["privateDnsName"], "Private DNS should be used if public DNS is not available" - -def test_given_isntance_has_blank_public_dns_ec2_then_dns_private_dns_is_used(full_ec2_config): - full_ec2_config["configuration"]["publicDnsName"] = "" - del(full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["association"]) - - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row was expected" - assert mapped_result[0].dns_name == full_ec2_config["configuration"]["privateDnsName"], "Private DNS should be used if public DNS is not available" - -def test_given_isntance_has_with_public_dns_ec2_then_dns_public_dns_is_used(full_ec2_config): - full_ec2_config["configuration"]["publicDnsName"] = "example.com" - - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row is expected. Two IP addresses are expected, one for the public IP and one for the private IP" - assert mapped_result[0].dns_name == full_ec2_config["configuration"]["publicDnsName"], "Public DNS should be used if public DNS is available" - -def test_given_ec2_instance_with_no_public_ip_then_one_item_returned(full_ec2_config): - del(full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["association"]) - - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row was expected since instance only has one private IP" - assert mapped_result[0].ip_address == full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["privateIpAddress"], "IP Address should match what was returned from config" - -def test_given_ec2_instance_with_public_ip_then_two_items_returned(full_ec2_config): - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row is expected. Two IP addresses are expected, one for the public IP and one for the private IP" - - # Make sure both ip addresses are in the mapping - privateIp, publicIp = mapped_result[0].ip_address.split(',') - assert full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["privateIpAddress"] == privateIp - assert full_ec2_config["configuration"]["networkInterfaces"][0]["privateIpAddresses"][0]["association"]["publicIp"] == publicIp - -def test_given_ec2_instance_with_public_dns_name_then_asset_is_marked_as_public(full_ec2_config): - mapper = EC2DataMapper() - - mapped_result = mapper.map(full_ec2_config) - - assert len(mapped_result) == 1, "One row is expected. Two IP addresses are expected, one for the public IP and one for the private IP" - assert mapped_result[0].is_public == "Yes", "Instance should have been marked public since it has a public DNS name" diff --git a/fedramp-integrated-inventory-workbook/tests/test_elastic_search_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_elastic_search_mapper.py deleted file mode 100644 index 87c87b22..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_elastic_search_mapper.py +++ /dev/null @@ -1,36 +0,0 @@ -import json -import os -import pytest -from inventory.mappers import ElasticSearchDataMapper - -@pytest.fixture() -def full_es_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_es.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_elastic_search_then_base_attributes_mapped(full_es_config): - mapper = ElasticSearchDataMapper() - - mapped_result = mapper.map(full_es_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].unique_id == full_es_config["arn"], "ARN should be mapped to unique id" - -def test_given_elastic_search_then_configuration_noted(full_es_config): - mapper = ElasticSearchDataMapper() - - mapped_result = mapper.map(full_es_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].baseline_config == full_es_config["configuration"]["elasticsearchVersion"], "ElasticSearch version should be noted" - -def test_given_elastic_search_then_user_friendly_name_recorded(full_es_config): - mapper = ElasticSearchDataMapper() - - mapped_result = mapper.map(full_es_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].asset_tag == full_es_config["resourceName"], "ElasticSearch resource name should be noted" - diff --git a/fedramp-integrated-inventory-workbook/tests/test_elb_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_elb_mapper.py deleted file mode 100644 index e60d02b0..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_elb_mapper.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -import json -import os -import pytest -from inventory.mappers import ElbDataMapper - -@pytest.fixture() -def full_classic_elb_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_classic_elb.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_classic_elb_then_base_attributes_mapped(full_classic_elb_config): - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_classic_elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].unique_id == full_classic_elb_config["arn"], "ARN should be mapped to unique id" - assert mapped_result[0].network_id == full_classic_elb_config["configuration"]["vpcid"], "VPC ID should be mapped to network id" - -def test_given_resource_type_is_not_classic_elb_then_empty_array_is_returned(full_classic_elb_config): - full_classic_elb_config["resourceType"] = "NOT ELB" - - mapper = ElbDataMapper() - - assert mapper.map(full_classic_elb_config) == [] - -def test_given_internet_facing_classic_elb_then_it_is_mapped_as_public(full_classic_elb_config): - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_classic_elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "Yes", "ELB should have been marked as public given internet facing scheme" - -def test_given_internal_classic_elb_then_it_is_not_mapped_as_public(full_classic_elb_config): - full_classic_elb_config["configuration"]["scheme"] = "internal" - - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_classic_elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "No", "ELB should have been marked as private" - -_sample_v2elb_file_contents = None - -@pytest.fixture -def full_v2elb_config(): - global _sample_v2elb_file_contents - - if not _sample_v2elb_file_contents: - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_v2elb.json")) as file_data: - _sample_v2elb_file_contents = file_data.read() - - return json.loads(_sample_v2elb_file_contents) - -def test_given_v2elb_then_base_attributes_mapped(full_v2elb_config): - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_v2elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].unique_id == full_v2elb_config["arn"], "ARN should be mapped to unique id" - assert mapped_result[0].network_id == full_v2elb_config["configuration"]["vpcId"], "VPC ID should be mapped to network id" - -def test_given_internet_facing_v2elb_then_it_is_mapped_as_public(full_v2elb_config): - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_v2elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "Yes", "ELB should have been marked as public given internet facing scheme" - -def test_given_internal_v2elb_then_it_is_not_mapped_as_public(full_v2elb_config): - full_v2elb_config["configuration"]["scheme"] = "internal" - - mapper = ElbDataMapper() - - mapped_result = mapper.map(full_v2elb_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "No", "ELB should have been marked as private" - diff --git a/fedramp-integrated-inventory-workbook/tests/test_inventory_reader.py b/fedramp-integrated-inventory-workbook/tests/test_inventory_reader.py deleted file mode 100644 index 5f77e5b7..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_inventory_reader.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -from botocore.exceptions import ClientError -from callee import String, Contains -import json -import os -from unittest.mock import MagicMock, Mock, patch, ANY -import pytest -from inventory.mappers import DataMapper -import inventory.readers -from inventory.readers import AwsConfigInventoryReader - -def setup_function(): - os.environ["ACCOUNT_LIST"] = '[ { "name": "foo", "id": "210987654321", "regions": ["us-east-2"]} ]' - os.environ["MANUAL_ENTRY_ITEMS"] = '[]' - os.environ["CROSS_ACCOUNT_ROLE_NAME"] = "foobar" - -def test_given_valid_arn_then_aws_partition_determined(): - mock_lambda_context = Mock() - mock_lambda_context.invoked_function_arn = "arn:aws:lambda:us-east-1:123456789012:function:testing" - - reader = AwsConfigInventoryReader(lambda_context=mock_lambda_context) - - aws_partition = reader._get_aws_partition() - - assert aws_partition == "aws", "Partition of ARN in context is aws" - -@patch("inventory.readers._logger", autospec=True) -def test_given_unsupported_resource_type_then_warning_is_logged(mock_logger): - mock_mapper = Mock(spec=DataMapper) - mock_mapper.can_map.return_value = False - mock_config_client_factory = Mock() - mock_config_client_factory.return_value \ - .select_resource_config \ - .return_value = { "NextToken": None, - "Results": [ json.dumps({ "resourceType": "foobar" }) ] } - - reader = AwsConfigInventoryReader(lambda_context=MagicMock(), mappers=[mock_mapper]) - reader._get_config_client = mock_config_client_factory - - all_inventory = reader.get_resources_from_all_accounts() - - assert len(all_inventory) == 0, "no inventory should be returned since there was nothing to map" - mock_logger.warning.assert_called_with(String() & Contains("skipping mapping")) - -@patch("inventory.readers._logger", autospec=True) -def test_given_error_from_boto_then_account_is_skipped_but_others_still_processed(mock_logger): - os.environ["ACCOUNT_LIST"] = '[ { "name": "foo", "id": "210987654321", "regions": ["us-east-2"] }, { "name": "bar", "id": "123456789012","regions": ["us-east-2"] }]' - mock_mapper = Mock(spec=DataMapper) - mock_mapper.can_map.return_value = True - mock_mapper.map.return_value = [ { "test": True }] - mock_select_resource_config = Mock(side_effect=[ ClientError(error_response={'Error': {'Code': 'ResourceInUseException'}}, operation_name="select_resource_config"), - { "NextToken": None, - "Results": [ json.dumps({ "resourceType": "foobar" }) ] }]) - mock_config_client_factory = Mock() - mock_config_client_factory.return_value \ - .select_resource_config = mock_select_resource_config - - reader = AwsConfigInventoryReader(lambda_context=MagicMock(), mappers=[mock_mapper]) - reader._get_config_client = mock_config_client_factory - - all_inventory = reader.get_resources_from_all_accounts() - - assert len(all_inventory) == 1, "inventory from the successful call should be returned" - assert len(mock_select_resource_config.mock_calls) == 2, "boto should have been called twice to page through results" - mock_logger.error.assert_called_with(String() & Contains("moving onto next account"), ANY, ANY, exc_info=True) - -def test_given_multiple_resource_pages_from_boto_then_reader_loops_through_all_pages(): - mock_mapper = Mock(spec=DataMapper) - mock_mapper.can_map.return_value = False - mock_select_resource_config = Mock(side_effect=[{ "NextToken": "nextpage", - "Results": [ json.dumps({ "resourceType": "foobar" }) ] }, - { "NextToken": None, - "Results": [ json.dumps({ "resourceType": "foobar" }) ] }]) - mock_config_client_factory = Mock() - mock_config_client_factory.return_value \ - .select_resource_config = mock_select_resource_config - - readerx = AwsConfigInventoryReader(lambda_context=MagicMock(), mappers=[mock_mapper]) - readerx._get_config_client = mock_config_client_factory - - all_inventory = readerx.get_resources_from_all_accounts() - - assert len(all_inventory) == 0, "no inventory should be returned since there was nothing to map" - assert len(mock_select_resource_config.mock_calls) == 2, "boto should have been called twice to page through results" - assert mock_select_resource_config.call_args.kwargs["NextToken"] == "nextpage", "NextToken must use value from previous select_resource_config call" diff --git a/fedramp-integrated-inventory-workbook/tests/test_lambda_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_lambda_mapper.py deleted file mode 100644 index 15039408..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_lambda_mapper.py +++ /dev/null @@ -1,35 +0,0 @@ -import json -import os -import pytest -from inventory.mappers import LambdaDataMapper - - -@pytest.fixture() -def full_lambda_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_lambda_function.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - - -def test_given_resource_type_is_not_lambda_then_empty_array_is_returned(full_lambda_config): - full_lambda_config["resourceType"] = "NOT Lambda" - - mapper = LambdaDataMapper() - - assert mapper.map(full_lambda_config) == [] - - full_lambda_config["resourceType"] = "AWS::Lambda::Function" - assert len(mapper.map(full_lambda_config)) > 0, "Resource should have been mapped" - - -def test_given_lambda_version_contains_sha(full_lambda_config): - mapper = LambdaDataMapper() - - assert full_lambda_config["configuration"]['codeSha256'] in mapper.map(full_lambda_config)[0].software_product_name - - -def test_given_resource_is_mapped_to_unique_id(full_lambda_config): - mapper = LambdaDataMapper() - - assert mapper.map(full_lambda_config)[0].unique_id == "arn:aws:lambda:us-west-2:123456789:function:InventoryCollector" diff --git a/fedramp-integrated-inventory-workbook/tests/test_rds_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_rds_mapper.py deleted file mode 100644 index 07e86879..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_rds_mapper.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -import json -import os -import pytest -from inventory.mappers import RdsDataMapper - -@pytest.fixture() -def full_rds_db_instance_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_rds_instance.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_fully_configured_rds_instance_then_base_attributes_are_mapped(full_rds_db_instance_config): - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].network_id == full_rds_db_instance_config['configuration']['dBSubnetGroup']['vpcId'], "NetworkId must be mapped if the VPC ID exists in the configuration" - assert mapped_result[0].hardware_model == full_rds_db_instance_config['configuration']['dBInstanceClass'] - assert mapped_result[0].software_product_name == f"{full_rds_db_instance_config['configuration']['engine']}-{full_rds_db_instance_config['configuration']['engineVersion']}" - -def test_given_rds_instance_marked_as_private_then_is_mapped_as_not_public(full_rds_db_instance_config): - full_rds_db_instance_config["configuration"]["publiclyAccessible"] = False - - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "No", "Instance must be marked as not public if publiclyAccessible is set to False" - -def test_given_rds_instance_marked_as_public_then_is_mapped_as_public(full_rds_db_instance_config): - full_rds_db_instance_config["configuration"]["publiclyAccessible"] = True - - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].is_public == "Yes", "Instance must be marked as public if publiclyAccessible is set to True" - -def test_given_rds_instance_with_no_subnet_group_then_networkid_is_left_blank(full_rds_db_instance_config): - del(full_rds_db_instance_config["configuration"]["dBSubnetGroup"]) - - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - - assert len(mapped_result) == 1, "Expected one row to be mapped" - assert mapped_result[0].network_id == "", "Instance must be marked as public if publiclyAccessible is set to True" - -def test_given_resource_is_mapped_to_region(full_rds_db_instance_config): - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - assert mapped_result[0].location == "us-west-2", "Resource should be contained in us-west-2" - - -def test_given_resource_configuration_contains_resource_specifications(full_rds_db_instance_config): - mapper = RdsDataMapper() - - mapped_result = mapper.map(full_rds_db_instance_config) - assert mapped_result[0].hardware_model == "db.r5.large", "Resource should contain a hardware model" - assert mapped_result[0].software_product_name == "aurora-mysql-5.7.mysql_aurora.2.07.2", "Resource should contain database software type" diff --git a/fedramp-integrated-inventory-workbook/tests/test_reports.py b/fedramp-integrated-inventory-workbook/tests/test_reports.py deleted file mode 100644 index 32c11407..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_reports.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# AWS DISCLAMER -# --- - -# The following files are provided by AWS Professional Services describe the process to create a IAM Policy with description. - -# These are non-production ready and are to be used for testing purposes. - -# These files is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES -# OR CONDITIONS OF ANY KIND, either express or implied. See the License -# for the specific language governing permissions and limitations under the License. - -# (c) 2019 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. -# This AWS Content is provided subject to the terms of the AWS Customer Agreement available at -# http://aws.amazon.com/agreement or other written agreement between Customer and Amazon Web Services, Inc.​ -import os -from unittest.mock import Mock, mock_open, patch, ANY -from callee import String, Contains -import pytest -import inventory.reports -from inventory.reports import CreateReportCommandHandler, DeliverReportCommandHandler - -@patch('inventory.reports.load_workbook') -def test_given_empty_inventory_list_then_report_is_still_written(mock_load_workbook): - report_handler = CreateReportCommandHandler() - - report_handler.execute([]) - - mock_load_workbook.return_value.save.assert_called() - -@patch('builtins.open') -@patch('inventory.reports.datetime') -def test_given_report_file_exists_then_delivery_to_s3_uses_correct_file_naming_convention(mock_datetime, mock_open): - test_bucket_name = "bucket" - os.environ["REPORT_TARGET_BUCKET_NAME"] = test_bucket_name - os.environ["REPORT_TARGET_BUCKET_PATH"] = "test/path" - mock_s3_client = Mock() - - report_handler = DeliverReportCommandHandler(s3_client=mock_s3_client) - report_url = report_handler.execute("somereport") - - # Only verifying that we try to format the datetime correctly as that's the most import part of the report file name - mock_datetime.now.return_value.strftime.assert_called_with("%Y-%m-%d-%H-%M-%S") - mock_s3_client.put_object.assert_called_with(Key=ANY, Bucket=test_bucket_name, Body=ANY) - assert report_url is not None and len(report_url) > 0, "report URL should be returned" \ No newline at end of file diff --git a/fedramp-integrated-inventory-workbook/tests/test_s3_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_s3_mapper.py deleted file mode 100644 index c9d83ace..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_s3_mapper.py +++ /dev/null @@ -1,48 +0,0 @@ -import json -import os -import pytest -from inventory.mappers import S3DataMapper - -@pytest.fixture() -def full_s3_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_s3.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_resource_type_is_not_s3_then_empty_array_is_returned(full_s3_config): - full_s3_config["resourceType"] = "NOT S3" - - mapper = S3DataMapper() - - assert mapper.map(full_s3_config) == [] - - full_s3_config["resourceType"] = "AWS::S3::Bucket" - assert len(mapper.map(full_s3_config)) > 0, "Resource should have been mapped" - -def test_given_resource_type_is_private_when_all_public_access_blocked(full_s3_config): - mapper = S3DataMapper() - - mapped_result = mapper.map(full_s3_config) - assert mapped_result[0].is_public == "No", "Bucket has no public access enabled" - - full_s3_config["supplementaryConfiguration"]["PublicAccessBlockConfiguration"]["blockPublicAcls"] = False - mapped_result = mapper.map(full_s3_config) - assert mapped_result[0].is_public == "Yes", "After changing the sample json, the bucket is no longer blocking all public access" - - # PublicAccessBlockConfiguration may not be present, verify that mapping still works if the key doesn't exist in the dictionary - full_s3_config["supplementaryConfiguration"].pop("PublicAccessBlockConfiguration", None) - mapped_result = mapper.map(full_s3_config) - assert mapped_result[0].is_public == "Yes", "Without PublicAccessBlockConfiguration, the bucket is public " - -def test_given_resource_type_is_specified_in_region(full_s3_config): - mapper = S3DataMapper() - - mapped_result = mapper.map(full_s3_config) - assert mapped_result[0].location == "us-west-2" - -def test_given_resource_type_is_commented_based_on_encryption_status(full_s3_config): - mapper = S3DataMapper() - - mapped_result = mapper.map(full_s3_config) - assert mapped_result[0].comments == "Not encrypted", "Bucket has no encryption settings" diff --git a/fedramp-integrated-inventory-workbook/tests/test_vpc_mapper.py b/fedramp-integrated-inventory-workbook/tests/test_vpc_mapper.py deleted file mode 100644 index a0fb5c9c..00000000 --- a/fedramp-integrated-inventory-workbook/tests/test_vpc_mapper.py +++ /dev/null @@ -1,27 +0,0 @@ -import json -import os -import pytest -from inventory.mappers import VPCDataMapper - -@pytest.fixture() -def full_vpc_config(): - with open(os.path.join(os.path.dirname(__file__), "sample_config_query_results/sample_vpc.json")) as file_data: - file_contents = file_data.read() - - return json.loads(file_contents) - -def test_given_resource_type_is_not_vpc_then_empty_array_is_returned(full_vpc_config): - full_vpc_config["resourceType"] = "NOT VPC" - - mapper = VPCDataMapper() - - assert mapper.map(full_vpc_config) == [] - - full_vpc_config["resourceType"] = "AWS::EC2::VPC" - - assert len(mapper.map(full_vpc_config)) > 0, "Resource should have been mapped" - -def test_given_resource_is_mapped_to_unique_id(full_vpc_config): - mapper = VPCDataMapper() - - assert mapper.map(full_vpc_config)[0].unique_id == "arn:aws:ec2:us-west-2:123456789:vpc/vpc-12345" From 9ed6183c8229c981829caabb25ed693bead4c131 Mon Sep 17 00:00:00 2001 From: David Steinberg Date: Wed, 8 May 2024 10:31:58 -0700 Subject: [PATCH 2/2] Remove circle tests for inventory --- .circleci/config.yml | 58 -------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 910efa33..298e8574 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,10 +35,6 @@ jobs: - create_zip_upload_to_s3: lambdaFolder: "webhook-testing/deployment" s3BucketFolder: "webhookTesting" - - create_fedramp_inventory_zip_upload_to_s3: - lambdaFolder: "fedramp-integrated-inventory-workbook/deployment" - lambdaRequirements: "fedramp-integrated-inventory-workbook/requirements.txt" - s3BucketFolder: "fedrampInventory" # Make this a separate job because AWS SAM needs to run in # the container specified in the template.yml @@ -77,14 +73,6 @@ jobs: - checkout - install_container_dependencies - unit_tests - python_tests: - docker: - - image: cimg/python:3.8 - steps: - - checkout - - python_unit_tests: - sourceDirectory: "fedramp-integrated-inventory-workbook/deployment" - dependencies: "fedramp-integrated-inventory-workbook/requirements.txt" commands: sam_package_and_upload_to_s3: @@ -123,31 +111,6 @@ commands: arguments: | --cache-control max-age=0 - create_fedramp_inventory_zip_upload_to_s3: - description: "Create a deployment zip for the fedramp inventory lambda and upload to S3" - parameters: - lambdaFolder: - type: string - lambdaRequirements: - type: string - s3BucketFolder: - type: string - steps: - - run: - name: make temporary directory and copy in source files - command: cd << parameters.lambdaFolder >> && mkdir output && cp -R inventory output - - run: - name: install all requirements into the temporary directory - command: pip install -r << parameters.lambdaRequirements >> -t << parameters.lambdaFolder >>/output -U --no-deps && chmod -R 755 << parameters.lambdaFolder >>/output - - run: - name: zip source files alongside dependencies - command: (cd << parameters.lambdaFolder >>/output && zip -r8 ../function.zip . && cd .. && rm -rf output) - - aws-s3/copy: - from: << parameters.lambdaFolder >>/function.zip - to: 's3://${AWS_BUCKET}/$(echo ${CIRCLE_TAG-$CIRCLE_BRANCH} | sed "s/\//_/g")-$(echo $CIRCLE_SHA1 | cut -c -7)/<< parameters.s3BucketFolder >>/function.zip' - arguments: | - --cache-control max-age=0 - check_git_secrets: description: "Install git-secrets and scan repository to check for secrets" steps: @@ -186,23 +149,6 @@ commands: name: run unit tests command: bash -i -c 'npm run test' - python_unit_tests: - description: "Run unit tests for the fedramp python lambda" - parameters: - sourceDirectory: - type: string - dependencies: - type: string - steps: - - run: - name: setup python test environment - command: pip install -r << parameters.dependencies >> && pip install pytest==6.2.2 callee==0.3.1 - - run: - name: run python unit tests - # The tests require you to have access to the python package, in this case, deployment/inventory/ - # which is why we change directories. - command: cd << parameters.sourceDirectory >> && python -m pytest -v -s ../tests - workflows: version: 2 everything: @@ -217,10 +163,6 @@ workflows: filters: tags: only: /.*/ - - python_tests: - filters: - tags: - only: /.*/ - sam_tests: filters: tags: