fix(global_services): handle global regions correctly (#1594)

Co-authored-by: sergargar <sergio@verica.io>
Co-authored-by: Pepe Fagoaga <pepe@verica.io>
This commit is contained in:
Sergio Garcia
2022-12-23 12:32:31 +01:00
committed by GitHub
parent 3cfe1b8376
commit d9dc6c0a49
25 changed files with 789 additions and 223 deletions

View File

@@ -103,58 +103,39 @@ def assume_role(audit_info: AWS_Audit_Info) -> dict:
return assumed_credentials
def generate_regional_clients(service: str, audit_info: AWS_Audit_Info) -> dict:
regional_clients = {}
# Get json locally
actual_directory = os.path.dirname(os.path.realpath(__file__))
f = open_file(f"{actual_directory}/{aws_services_json_file}")
data = parse_json_file(f)
# Check if it is a subservice
if service == "accessanalyzer":
json_regions = data["services"]["iam"]["regions"][audit_info.audited_partition]
elif service == "apigatewayv2":
json_regions = data["services"]["apigateway"]["regions"][
audit_info.audited_partition
]
elif service == "macie2":
json_regions = data["services"]["macie"]["regions"][
audit_info.audited_partition
]
elif service == "logs":
json_regions = data["services"]["cloudwatch"]["regions"][
audit_info.audited_partition
]
elif service == "dax":
json_regions = data["services"]["dynamodb"]["regions"][
audit_info.audited_partition
]
elif service == "glacier":
json_regions = data["services"]["s3"]["regions"][audit_info.audited_partition]
elif service == "opensearch":
json_regions = data["services"]["es"]["regions"][audit_info.audited_partition]
elif service == "elbv2":
json_regions = data["services"]["elb"]["regions"][audit_info.audited_partition]
elif service == "wafv2" or service == "waf-regional":
json_regions = data["services"]["waf"]["regions"][audit_info.audited_partition]
else:
def generate_regional_clients(
service: str, audit_info: AWS_Audit_Info, global_service: bool = False
) -> dict:
try:
regional_clients = {}
# Get json locally
actual_directory = os.path.dirname(os.path.realpath(__file__))
f = open_file(f"{actual_directory}/{aws_services_json_file}")
data = parse_json_file(f)
# Check if it is a subservice
json_regions = data["services"][service]["regions"][
audit_info.audited_partition
]
if audit_info.audited_regions: # Check for input aws audit_info.audited_regions
regions = list(
set(json_regions).intersection(audit_info.audited_regions)
) # Get common regions between input and json
else: # Get all regions from json of the service and partition
regions = json_regions
for region in regions:
regional_client = audit_info.audit_session.client(service, region_name=region)
regional_client.region = region
regional_clients[region] = regional_client
return regional_clients
def get_region_global_service(audit_info: AWS_Audit_Info) -> str:
# Check if global service to send the finding to first audited region
if audit_info.audited_regions:
return audit_info.audited_regions[0]
return audit_info.profile_region
if audit_info.audited_regions: # Check for input aws audit_info.audited_regions
regions = list(
set(json_regions).intersection(audit_info.audited_regions)
) # Get common regions between input and json
else: # Get all regions from json of the service and partition
regions = json_regions
# Check if it is global service to gather only one region
if global_service:
if regions:
if audit_info.profile_region in regions:
regions = [audit_info.profile_region]
regions = regions[:1]
for region in regions:
regional_client = audit_info.audit_session.client(
service, region_name=region
)
regional_client.region = region
regional_clients[region] = regional_client
return regional_clients
except Exception as error:
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

View File

@@ -1,5 +1,45 @@
{
"services": {
"accessanalyzer": {
"regions": {
"aws": [
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"eu-north-1",
"us-west-1",
"af-south-1",
"ap-northeast-1",
"ca-central-1",
"eu-central-1",
"eu-south-2",
"eu-west-2",
"me-central-1",
"me-south-1",
"us-east-2",
"us-west-2",
"ap-east-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"eu-central-2",
"eu-south-1",
"eu-west-1",
"eu-west-3",
"sa-east-1",
"us-east-1"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"acm": {
"regions": {
"aws": [
@@ -211,6 +251,46 @@
]
}
},
"apigatewayv2": {
"regions": {
"aws": [
"af-south-1",
"ap-east-1",
"ap-northeast-3",
"eu-central-1",
"eu-north-1",
"eu-south-2",
"sa-east-1",
"us-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-southeast-2",
"ap-southeast-3",
"eu-south-1",
"eu-west-1",
"eu-west-2",
"me-central-1",
"us-east-2",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ca-central-1",
"eu-central-2",
"eu-west-3",
"me-south-1",
"us-west-1",
"us-west-2"
],
"aws-cn": [
"cn-northwest-1",
"cn-north-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"appflow": {
"regions": {
"aws": [
@@ -1567,6 +1647,46 @@
]
}
},
"dax": {
"regions": {
"aws": [
"ap-south-2",
"ap-southeast-3",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-west-2",
"me-south-1",
"ap-east-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-southeast-2",
"eu-south-2",
"eu-west-1",
"me-central-1",
"us-east-2",
"us-west-2",
"af-south-1",
"ap-northeast-3",
"ap-south-1",
"ap-southeast-1",
"eu-south-1",
"eu-west-3",
"sa-east-1",
"us-east-1",
"us-west-1"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"deepcomposer": {
"regions": {
"aws": [
@@ -2271,6 +2391,46 @@
]
}
},
"elbv2": {
"regions": {
"aws": [
"ap-northeast-1",
"ap-south-2",
"eu-north-1",
"eu-south-1",
"eu-west-2",
"me-central-1",
"me-south-1",
"us-east-2",
"us-west-2",
"ap-northeast-2",
"ap-northeast-3",
"ap-south-1",
"ap-southeast-3",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-3",
"sa-east-1",
"us-east-1",
"af-south-1",
"ap-east-1",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-south-2",
"us-west-1"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"emr": {
"regions": {
"aws": [
@@ -2769,6 +2929,46 @@
"aws-us-gov": {}
}
},
"glacier": {
"regions": {
"aws": [
"ap-east-1",
"ap-northeast-1",
"eu-central-1",
"eu-west-2",
"eu-west-3",
"me-central-1",
"sa-east-1",
"us-west-1",
"ap-northeast-3",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"eu-south-2",
"us-east-1",
"af-south-1",
"ap-northeast-2",
"ca-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-west-1",
"me-south-1",
"us-east-2",
"us-west-2"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"globalaccelerator": {
"regions": {
"aws": [
@@ -3511,19 +3711,20 @@
"lex-runtime": {
"regions": {
"aws": [
"af-south-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-southeast-1",
"ap-southeast-2",
"ca-central-1",
"eu-central-1",
"eu-west-1",
"eu-west-2",
"us-east-1",
"us-west-2"
"us-west-2",
"ap-southeast-2"
],
"aws-cn": {},
"aws-us-gov": [
"us-gov-west-1"
]
"aws-us-gov": {}
}
},
"license-manager": {
@@ -3584,6 +3785,46 @@
"aws-us-gov": {}
}
},
"logs": {
"regions": {
"aws": [
"ap-northeast-2",
"ap-south-2",
"ca-central-1",
"me-central-1",
"me-south-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"af-south-1",
"ap-east-1",
"ap-northeast-1",
"ap-northeast-3",
"ap-southeast-3",
"eu-central-2",
"eu-north-1",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-central-1",
"eu-south-1",
"eu-south-2",
"eu-west-1",
"eu-west-2",
"eu-west-3"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-west-1",
"us-gov-east-1"
]
}
},
"lookoutmetrics": {
"regions": {
"aws": [
@@ -3691,6 +3932,35 @@
"aws-us-gov": {}
}
},
"macie2": {
"regions": {
"aws": [
"ap-northeast-1",
"ap-northeast-2",
"ap-south-1",
"ap-southeast-1",
"ap-southeast-2",
"eu-central-1",
"eu-north-1",
"eu-west-1",
"us-east-1",
"us-east-2",
"af-south-1",
"ap-east-1",
"ap-northeast-3",
"ca-central-1",
"eu-west-2",
"eu-west-3",
"me-south-1",
"sa-east-1",
"us-west-1",
"us-west-2",
"eu-south-1"
],
"aws-cn": {},
"aws-us-gov": {}
}
},
"managedblockchain": {
"regions": {
"aws": [
@@ -4113,7 +4383,7 @@
"ap-northeast-3",
"ap-southeast-1",
"ap-southeast-2",
"eu-west-2",
"ap-southeast-3",
"eu-west-3",
"me-south-1",
"us-east-2",
@@ -4122,11 +4392,12 @@
"ap-northeast-2",
"ap-south-1",
"ca-central-1",
"eu-central-1",
"eu-north-1",
"eu-south-1",
"eu-west-1",
"eu-west-2",
"us-east-1",
"eu-central-1",
"sa-east-1",
"us-west-2"
],
@@ -4151,6 +4422,46 @@
"aws-us-gov": {}
}
},
"opensearch": {
"regions": {
"aws": [
"ap-east-1",
"ap-south-1",
"ap-southeast-2",
"eu-south-1",
"eu-south-2",
"eu-west-3",
"us-west-1",
"us-west-2",
"af-south-1",
"ap-south-2",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-north-1",
"eu-west-1",
"us-east-1",
"us-east-2",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-southeast-1",
"ap-southeast-3",
"eu-west-2",
"me-central-1",
"me-south-1",
"sa-east-1"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-west-1",
"us-gov-east-1"
]
}
},
"opsworks": {
"regions": {
"aws": [
@@ -4762,6 +5073,43 @@
"aws-us-gov": {}
}
},
"route53domains": {
"regions": {
"aws": [
"ap-east-1",
"ap-northeast-3",
"ap-south-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"sa-east-1",
"us-west-1",
"af-south-1",
"ap-northeast-2",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"eu-north-1",
"eu-south-1",
"eu-south-2",
"eu-west-3",
"ap-northeast-1",
"ca-central-1",
"me-central-1",
"me-south-1",
"us-east-1",
"us-east-2",
"us-west-2"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": {}
}
},
"s3": {
"regions": {
"aws": [
@@ -4802,6 +5150,46 @@
]
}
},
"s3control": {
"regions": {
"aws": [
"ap-east-1",
"ap-northeast-1",
"eu-central-1",
"eu-west-2",
"eu-west-3",
"me-central-1",
"sa-east-1",
"us-west-1",
"ap-northeast-3",
"ap-south-1",
"ap-south-2",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"eu-south-2",
"us-east-1",
"af-south-1",
"ap-northeast-2",
"ca-central-1",
"eu-central-2",
"eu-north-1",
"eu-south-1",
"eu-west-1",
"me-south-1",
"us-east-2",
"us-west-2"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-east-1",
"us-gov-west-1"
]
}
},
"sagemaker": {
"regions": {
"aws": [
@@ -5004,6 +5392,7 @@
"ap-southeast-1",
"ap-southeast-2",
"eu-south-1",
"eu-south-2",
"eu-west-2",
"eu-west-3",
"us-west-1",
@@ -5013,14 +5402,16 @@
"ca-central-1",
"eu-central-1",
"eu-north-1",
"me-south-1",
"us-east-2",
"us-west-2",
"ap-east-1",
"ap-northeast-2",
"ap-south-1",
"ap-south-2",
"eu-central-2",
"eu-west-1",
"me-central-1",
"me-south-1",
"sa-east-1",
"us-east-1"
],
@@ -5029,8 +5420,8 @@
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-west-1",
"us-gov-east-1"
"us-gov-east-1",
"us-gov-west-1"
]
}
},
@@ -5915,6 +6306,78 @@
]
}
},
"waf-regional": {
"regions": {
"aws": [
"af-south-1",
"ap-east-1",
"ap-southeast-3",
"eu-central-1",
"eu-north-1",
"eu-west-1",
"eu-west-2",
"me-south-1",
"us-west-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-southeast-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-2",
"ap-south-1",
"ap-southeast-2",
"ca-central-1",
"eu-south-1",
"eu-west-3"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-west-1",
"us-gov-east-1"
]
}
},
"wafv2": {
"regions": {
"aws": [
"af-south-1",
"ap-east-1",
"ap-southeast-3",
"eu-central-1",
"eu-north-1",
"eu-west-1",
"eu-west-2",
"me-south-1",
"us-west-1",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-southeast-1",
"sa-east-1",
"us-east-1",
"us-east-2",
"us-west-2",
"ap-south-1",
"ap-southeast-2",
"ca-central-1",
"eu-south-1",
"eu-west-3"
],
"aws-cn": [
"cn-north-1",
"cn-northwest-1"
],
"aws-us-gov": [
"us-gov-west-1",
"us-gov-east-1"
]
}
},
"wam": {
"regions": {
"aws": [

View File

@@ -1,5 +1,4 @@
################## Account
from prowler.providers.aws.aws_provider import get_region_global_service
class Account:
@@ -7,7 +6,7 @@ class Account:
self.service = "account"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.region = get_region_global_service(audit_info)
self.region = audit_info.profile_region
def __get_session__(self):
return self.session

View File

@@ -2,7 +2,7 @@ from dataclasses import dataclass
from enum import Enum
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import get_region_global_service
from prowler.providers.aws.aws_provider import generate_regional_clients
################## CloudFront
@@ -11,12 +11,17 @@ class CloudFront:
self.service = "cloudfront"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.client = self.session.client(self.service)
self.region = get_region_global_service(audit_info)
self.distributions = self.__list_distributions__(self.client, self.region)
self.distributions = self.__get_distribution_config__(
self.client, self.distributions, self.region
global_client = generate_regional_clients(
self.service, audit_info, global_service=True
)
self.distributions = {}
if global_client:
self.client = list(global_client.values())[0]
self.region = self.client.region
self.distributions = self.__list_distributions__(self.client, self.region)
self.distributions = self.__get_distribution_config__(
self.client, self.distributions, self.region
)
def __get_session__(self):
return self.session

View File

@@ -3,10 +3,7 @@ import threading
from dataclasses import dataclass
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import (
generate_regional_clients,
get_region_global_service,
)
from prowler.providers.aws.aws_provider import generate_regional_clients
################### CLOUDTRAIL
@@ -15,7 +12,7 @@ class Cloudtrail:
self.service = "cloudtrail"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.region = get_region_global_service(audit_info)
self.region = audit_info.profile_region
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.trails = []
self.__threading_call__(self.__get_trails__)

View File

@@ -9,13 +9,14 @@ class GlobalAccelerator:
self.service = "globalaccelerator"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
# Global Accelerator is a global service that supports endpoints in multiple AWS Regions
# but you must specify the US West (Oregon) Region to create, update, or otherwise work with accelerators.
# That is, for example, specify --region us-west-2 on AWS CLI commands.
self.region = "us-west-2"
self.client = self.session.client(self.service, self.region)
self.accelerators = {}
self.__list_accelerators__()
if audit_info.audited_partition == "aws":
# Global Accelerator is a global service that supports endpoints in multiple AWS Regions
# but you must specify the US West (Oregon) Region to create, update, or otherwise work with accelerators.
# That is, for example, specify --region us-west-2 on AWS CLI commands.
self.region = "us-west-2"
self.client = self.session.client(self.service, self.region)
self.__list_accelerators__()
def __get_session__(self):
return self.session

View File

@@ -3,7 +3,7 @@ from dataclasses import dataclass
from datetime import datetime
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import get_region_global_service
from prowler.providers.aws.aws_provider import generate_regional_clients
################## IAM
@@ -14,7 +14,11 @@ class IAM:
self.account = audit_info.audited_account
self.partition = audit_info.audited_partition
self.client = self.session.client(self.service)
self.region = get_region_global_service(audit_info)
global_client = generate_regional_clients(
self.service, audit_info, global_service=True
)
self.client = list(global_client.values())[0]
self.region = self.client.region
self.users = self.__get_users__()
self.roles = self.__get_roles__()
self.account_summary = self.__get_account_summary__()

View File

@@ -1,7 +1,7 @@
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import get_region_global_service
from prowler.providers.aws.aws_provider import generate_regional_clients
################## Route53
@@ -10,11 +10,15 @@ class Route53:
self.service = "route53"
self.session = audit_info.audit_session
self.audited_partition = audit_info.audited_partition
self.client = self.session.client(self.service)
self.region = get_region_global_service(audit_info)
self.hosted_zones = {}
self.__list_hosted_zones__()
self.__list_query_logging_configs__()
global_client = generate_regional_clients(
self.service, audit_info, global_service=True
)
if global_client:
self.client = list(global_client.values())[0]
self.region = self.client.region
self.__list_hosted_zones__()
self.__list_query_logging_configs__()
def __get_session__(self):
return self.session
@@ -84,13 +88,14 @@ class Route53Domains:
self.service = "route53domains"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
# Route53Domains is a global service that supports endpoints in multiple AWS Regions
# but you must specify the US East (N. Virginia) Region to create, update, or otherwise work with domains.
self.region = "us-east-1"
self.client = self.session.client(self.service, self.region)
self.domains = {}
self.__list_domains__()
self.__get_domain_detail__()
if audit_info.audited_partition == "aws":
# Route53Domains is a global service that supports endpoints in multiple AWS Regions
# but you must specify the US East (N. Virginia) Region to create, update, or otherwise work with domains.
self.region = "us-east-1"
self.client = self.session.client(self.service, self.region)
self.__list_domains__()
self.__get_domain_detail__()
def __get_session__(self):
return self.session

View File

@@ -3,10 +3,7 @@ import threading
from dataclasses import dataclass
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import (
generate_regional_clients,
get_region_global_service,
)
from prowler.providers.aws.aws_provider import generate_regional_clients
################## S3
@@ -203,9 +200,13 @@ class S3Control:
self.service = "s3control"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.region = get_region_global_service(audit_info)
self.client = self.session.client(self.service, self.region)
self.account_public_access_block = self.__get_public_access_block__()
global_client = generate_regional_clients(
self.service, audit_info, global_service=True
)
if global_client:
self.client = list(global_client.values())[0]
self.region = self.client.region
self.account_public_access_block = self.__get_public_access_block__()
def __get_session__(self):
return self.session

View File

@@ -1,48 +0,0 @@
#!/usr/bin/env bash
# Prowler - the handy cloud security tool (copyright 2019) by Toni de la Fuente
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License 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.
CHECK_ID_extra7170="7.170"
CHECK_TITLE_extra7170="[extra7170] Check if internet-facing application load balancers are protected by AWS Shield Advanced"
CHECK_SCORED_extra7170="NOT_SCORED"
CHECK_CIS_LEVEL_extra7170="EXTRA"
CHECK_SEVERITY_extra7170="Medium"
CHECK_ASFF_RESOURCE_TYPE_extra7170="AwsElasticLoadBalancingV2LoadBalancer"
CHECK_ALTERNATE_check7170="extra7170"
CHECK_SERVICENAME_extra7170="shield"
CHECK_RISK_extra7170='AWS Shield Advanced provides expanded DDoS attack protection for your resources'
CHECK_REMEDIATION_extra7170='Add as a protected resource in AWS Shield Advanced.'
CHECK_DOC_extra7170='https://docs.aws.amazon.com/waf/latest/developerguide/configure-new-protection.html'
CHECK_CAF_EPIC_extra7170='Infrastructure security'
extra7170() {
if [[ "$($AWSCLI $PROFILE_OPT shield get-subscription-state --output text)" == "ACTIVE" ]]; then
for regx in $REGIONS; do
LIST_OF_APPLICATION_LOAD_BALANCERS=$($AWSCLI elbv2 describe-load-balancers $PROFILE_OPT --region $regx --query 'LoadBalancers[?Type == `application` && Scheme == `internet-facing`].[LoadBalancerName,LoadBalancerArn]' --output text)
if [[ $LIST_OF_APPLICATION_LOAD_BALANCERS ]]; then
while read -r alb; do
ALB_NAME=$(echo $alb | awk '{ print $1; }')
ALB_ARN=$(echo $alb | awk '{ print $2; }')
if $AWSCLI $PROFILE_OPT shield describe-protection --resource-arn $ALB_ARN >/dev/null 2>&1; then
textPass "$regx: ALB $ALB_NAME is protected by AWS Shield Advanced" "$regx" "$ALB_NAME"
else
textFail "$regx: ALB $ALB_NAME is not protected by AWS Shield Advanced" "$regx" "$ALB_NAME"
fi
done <<<"$LIST_OF_APPLICATION_LOAD_BALANCERS"
else
textInfo "$regx: no application load balancers found" "$regx"
fi
done
else
textInfo "No AWS Shield Advanced subscription found. Skipping check."
fi
}

View File

@@ -1,7 +1,7 @@
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import get_region_global_service
from prowler.providers.aws.aws_provider import generate_regional_clients
################### Shield
@@ -10,11 +10,17 @@ class Shield:
self.service = "shield"
self.session = audit_info.audit_session
self.audited_account = audit_info.audited_account
self.client = self.session.client(self.service)
self.region = get_region_global_service(audit_info)
self.enabled = self.__get_subscription_state__()
global_client = generate_regional_clients(
self.service, audit_info, global_service=True
)
self.protections = {}
self.__list_protections__()
self.enabled = False
if global_client:
self.client = list(global_client.values())[0]
self.region = self.client.region
self.enabled = self.__get_subscription_state__()
if self.enabled:
self.__list_protections__()
def __get_session__(self):
return self.session

View File

@@ -7,17 +7,22 @@ from prowler.providers.aws.services.trustedadvisor.trustedadvisor_client import
class trustedadvisor_errors_and_warnings(Check):
def execute(self):
findings = []
if trustedadvisor_client.checks:
for check in trustedadvisor_client.checks:
report = Check_Report_AWS(self.metadata())
report.region = check.region
report.resource_id = check.id
report.status = "FAIL"
report.status_extended = (
f"Trusted Advisor check {check.name} is in state {check.status}."
)
if check.status == "ok":
report.status = "PASS"
findings.append(report)
if trustedadvisor_client.enabled:
if trustedadvisor_client.checks:
for check in trustedadvisor_client.checks:
report = Check_Report_AWS(self.metadata())
report.region = check.region
report.resource_id = check.id
report.status = "FAIL"
report.status_extended = f"Trusted Advisor check {check.name} is in state {check.status}."
if check.status == "ok":
report.status = "PASS"
findings.append(report)
else:
report = Check_Report_AWS(self.metadata())
report.status = "INFO"
report.status_extended = "Amazon Web Services Premium Support Subscription is required to use this service."
report.resource_id = trustedadvisor_client.account
findings.append(report)
return findings

View File

@@ -1,10 +1,9 @@
import threading
from typing import Optional
from botocore.client import ClientError
from pydantic import BaseModel
from prowler.lib.logger import logger
from prowler.providers.aws.aws_provider import generate_regional_clients
################################ TrustedAdvisor
@@ -12,54 +11,64 @@ class TrustedAdvisor:
def __init__(self, audit_info):
self.service = "support"
self.session = audit_info.audit_session
self.regional_clients = generate_regional_clients(self.service, audit_info)
self.account = audit_info.audited_account
self.checks = []
self.__threading_call__(self.__describe_trusted_advisor_checks__)
self.__threading_call__(self.__describe_trusted_advisor_check_result__)
self.enabled = True
# Support API is not available in China Partition
# But only in us-east-1 or us-gov-west-1 https://docs.aws.amazon.com/general/latest/gr/awssupport.html
if audit_info.audited_partition != "aws-cn":
if audit_info.audited_partition == "aws":
support_region = "us-east-1"
else:
support_region = "us-gov-west-1"
self.client = audit_info.audit_session.client(
self.service, region_name=support_region
)
self.client.region = self.region = support_region
self.__describe_trusted_advisor_checks__()
self.__describe_trusted_advisor_check_result__()
def __get_session__(self):
return self.session
def __threading_call__(self, call):
threads = []
for regional_client in self.regional_clients.values():
threads.append(threading.Thread(target=call, args=(regional_client,)))
for t in threads:
t.start()
for t in threads:
t.join()
def __describe_trusted_advisor_checks__(self, regional_client):
def __describe_trusted_advisor_checks__(self):
logger.info("TrustedAdvisor - Describing Checks...")
try:
for check in regional_client.describe_trusted_advisor_checks(language="en")[
for check in self.client.describe_trusted_advisor_checks(language="en")[
"checks"
]:
self.checks.append(
Check(
id=check["id"],
name=check["name"],
region=regional_client.region,
region=self.client.region,
)
)
except ClientError as error:
if error.response["Error"]["Code"] == "SubscriptionRequiredException":
self.enabled = False
else:
logger.error(
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
def __describe_trusted_advisor_check_result__(self, regional_client):
def __describe_trusted_advisor_check_result__(self):
logger.info("TrustedAdvisor - Describing Check Result...")
try:
for check in self.checks:
if check.region == regional_client.region:
response = regional_client.describe_trusted_advisor_check_result(
if check.region == self.client.region:
response = self.client.describe_trusted_advisor_check_result(
checkId=check.id
)
if "result" in response:
check.status = response["result"]["status"]
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
f"{self.client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)

View File

@@ -2,7 +2,7 @@ import boto3
import sure # noqa
from moto import mock_iam, mock_sts
from prowler.providers.aws.aws_provider import assume_role, get_region_global_service
from prowler.providers.aws.aws_provider import assume_role, generate_regional_clients
from prowler.providers.aws.lib.audit_info.models import AWS_Assume_Role, AWS_Audit_Info
ACCOUNT_ID = 123456789012
@@ -82,24 +82,85 @@ class Test_AWS_Provider:
21 + 1 + len(sessionName)
)
def test_get_region_global_service(self):
# Create mock audit_info
input_audit_info = AWS_Audit_Info(
def test_generate_regional_clients(self):
# New Boto3 session with the previously create user
session = boto3.session.Session(
region_name="us-east-1",
)
audited_regions = ["eu-west-1", "us-east-1"]
# Fulfil the input session object for Prowler
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=None,
audited_account="123456789012",
audited_identity_arn="test-arn",
audited_user_id="test",
audit_session=session,
audited_account=None,
audited_partition="aws",
profile="default",
profile_region="eu-west-1",
audited_identity_arn=None,
audited_user_id=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=["eu-west-2", "eu-west-1"],
audited_regions=audited_regions,
organizations_metadata=None,
)
assert (
get_region_global_service(input_audit_info)
== input_audit_info.audited_regions[0]
generate_regional_clients_response = generate_regional_clients(
"ec2", audit_info
)
assert set(generate_regional_clients_response.keys()) == set(audited_regions)
def test_generate_regional_clients_global_service(self):
# New Boto3 session with the previously create user
session = boto3.session.Session(
region_name="us-east-1",
)
audited_regions = ["eu-west-1", "us-east-1"]
profile_region = "us-east-1"
# Fulfil the input session object for Prowler
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session,
audited_account=None,
audited_partition="aws",
audited_identity_arn=None,
audited_user_id=None,
profile=None,
profile_region=profile_region,
credentials=None,
assumed_role_info=None,
audited_regions=audited_regions,
organizations_metadata=None,
)
generate_regional_clients_response = generate_regional_clients(
"route53", audit_info, global_service=True
)
assert list(generate_regional_clients_response.keys()) == [profile_region]
def test_generate_regional_clients_cn_partition(self):
# New Boto3 session with the previously create user
session = boto3.session.Session(
region_name="us-east-1",
)
audited_regions = ["cn-northwest-1", "cn-north-1"]
# Fulfil the input session object for Prowler
audit_info = AWS_Audit_Info(
original_session=None,
audit_session=session,
audited_account=None,
audited_partition="aws-cn",
audited_identity_arn=None,
audited_user_id=None,
profile=None,
profile_region=None,
credentials=None,
assumed_role_info=None,
audited_regions=audited_regions,
organizations_metadata=None,
)
generate_regional_clients_response = generate_regional_clients(
"shield", audit_info, global_service=True
)
# Shield does not exist in China
assert generate_regional_clients_response == {}

View File

@@ -17,6 +17,8 @@ class Test_iam_administrator_access_with_mfa_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
@@ -55,6 +57,8 @@ class Test_iam_administrator_access_with_mfa_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
@@ -88,6 +92,8 @@ class Test_iam_administrator_access_with_mfa_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
@@ -123,6 +129,8 @@ class Test_iam_administrator_access_with_mfa_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),
@@ -182,6 +190,8 @@ class Test_iam_administrator_access_with_mfa_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_administrator_access_with_mfa.iam_administrator_access_with_mfa.iam_client",
new=IAM(current_audit_info),

View File

@@ -18,6 +18,8 @@ class Test_iam_avoid_root_usage:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
@@ -51,6 +53,8 @@ class Test_iam_avoid_root_usage:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
@@ -83,6 +87,8 @@ class Test_iam_avoid_root_usage:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),
@@ -115,6 +121,8 @@ class Test_iam_avoid_root_usage:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_avoid_root_usage.iam_avoid_root_usage.iam_client",
new=IAM(current_audit_info),

View File

@@ -44,6 +44,8 @@ nTTxU4a7x1naFxzYXK1iQ1vMARKMjDb19QEJIEJKZlDK4uS7yMlf1nFS
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_check_saml_providers_sts.iam_check_saml_providers_sts.iam_client",
new=IAM(current_audit_info),

View File

@@ -18,6 +18,8 @@ class Test_iam_disable_30_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -48,6 +50,8 @@ class Test_iam_disable_30_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -75,6 +79,8 @@ class Test_iam_disable_30_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_30_days_credentials.iam_disable_30_days_credentials.iam_client",
new=IAM(current_audit_info),

View File

@@ -18,6 +18,8 @@ class Test_iam_disable_45_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -48,6 +50,8 @@ class Test_iam_disable_45_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -75,6 +79,8 @@ class Test_iam_disable_45_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_45_days_credentials.iam_disable_45_days_credentials.iam_client",
new=IAM(current_audit_info),

View File

@@ -18,6 +18,8 @@ class Test_iam_disable_90_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -48,6 +50,8 @@ class Test_iam_disable_90_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),
@@ -75,6 +79,8 @@ class Test_iam_disable_90_days_credentials_test:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_disable_90_days_credentials.iam_disable_90_days_credentials.iam_client",
new=IAM(current_audit_info),

View File

@@ -24,6 +24,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(current_audit_info),
@@ -59,6 +61,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(current_audit_info),
@@ -97,6 +101,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(current_audit_info),
@@ -132,6 +138,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(current_audit_info),
@@ -179,6 +187,8 @@ class Test_iam_no_custom_policy_permissive_role_assumption:
from prowler.providers.aws.lib.audit_info.audit_info import current_audit_info
from prowler.providers.aws.services.iam.iam_service import IAM
current_audit_info.audited_partition = "aws"
with mock.patch(
"prowler.providers.aws.services.iam.iam_no_custom_policy_permissive_role_assumption.iam_no_custom_policy_permissive_role_assumption.iam_client",
new=IAM(current_audit_info),

View File

@@ -2,11 +2,11 @@ import json
from json import dumps
from boto3 import client, session
from freezegun import freeze_time
from moto import mock_iam
from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info
from prowler.providers.aws.services.iam.iam_service import IAM
from freezegun import freeze_time
AWS_ACCOUNT_NUMBER = 123456789012
TEST_DATETIME = "2023-01-01T12:01:01+00:00"
@@ -23,10 +23,10 @@ class Test_IAM_Service:
),
audited_account=None,
audited_user_id=None,
audited_partition=None,
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
profile_region="us-east-1",
credentials=None,
assumed_role_info=None,
audited_regions=None,

View File

@@ -14,6 +14,8 @@ class Test_trustedadvisor_errors_and_warnings:
def test_no_detectors(self):
trustedadvisor_client = mock.MagicMock
trustedadvisor_client.checks = []
trustedadvisor_client.enabled = False
trustedadvisor_client.account = AWS_ACCOUNT_NUMBER
with mock.patch(
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.TrustedAdvisor",
trustedadvisor_client,
@@ -24,11 +26,16 @@ class Test_trustedadvisor_errors_and_warnings:
check = trustedadvisor_errors_and_warnings()
result = check.execute()
assert len(result) == 0
assert len(result) == 1
assert (
result[0].status_extended
== "Amazon Web Services Premium Support Subscription is required to use this service."
)
def test_trustedadvisor_all_passed_checks(self):
trustedadvisor_client = mock.MagicMock
trustedadvisor_client.checks = []
trustedadvisor_client.enabled = True
trustedadvisor_client.checks.append(
Check(
id="check1",
@@ -55,6 +62,7 @@ class Test_trustedadvisor_errors_and_warnings:
def test_trustedadvisor_error_check(self):
trustedadvisor_client = mock.MagicMock
trustedadvisor_client.checks = []
trustedadvisor_client.enabled = True
trustedadvisor_client.checks.append(
Check(
id="check1",

View File

@@ -21,17 +21,7 @@ def mock_make_api_call(self, operation_name, kwarg):
return make_api_call(self, operation_name, kwarg)
def mock_generate_regional_clients(service, audit_info):
regional_client = audit_info.audit_session.client(service, region_name=AWS_REGION)
regional_client.region = AWS_REGION
return {AWS_REGION: regional_client}
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
@patch(
"prowler.providers.aws.services.trustedadvisor.trustedadvisor_service.generate_regional_clients",
new=mock_generate_regional_clients,
)
class Test_TrustedAdvisor_Service:
# Mocked Audit Info
def set_mocked_audit_info(self):
@@ -46,7 +36,7 @@ class Test_TrustedAdvisor_Service:
audited_partition="aws",
audited_identity_arn=None,
profile=None,
profile_region=None,
profile_region=AWS_REGION,
credentials=None,
assumed_role_info=None,
audited_regions=None,
@@ -64,8 +54,7 @@ class Test_TrustedAdvisor_Service:
def test_client(self):
audit_info = self.set_mocked_audit_info()
trustedadvisor = TrustedAdvisor(audit_info)
for reg_client in trustedadvisor.regional_clients.values():
assert reg_client.__class__.__name__ == "Support"
assert trustedadvisor.client.__class__.__name__ == "Support"
# Test TrustedAdvisor session
def test__get_session__(self):

View File

@@ -20,7 +20,8 @@ logging.basicConfig(
with request.urlopen(aws_services_json_url) as url: # Get the AWS regions matrix online
logging.info(f"Downloading JSON from {aws_services_json_url}")
original_matrix_regions_aws = json.loads(url.read().decode())
parsed__matrix_regions_aws = f"{os.path.dirname(os.path.realpath(__name__))}/prowler/providers/aws/aws_regions_by_service.json"
parsed_matrix_regions_aws = f"{os.path.dirname(os.path.realpath(__name__))}/prowler/providers/aws/aws_regions_by_service.json"
# JSON objects
regions_by_service = {}
@@ -66,7 +67,38 @@ for item in original_matrix_regions_aws["prices"]:
logging.info("Storing final JSON")
regions_by_service["services"] = services
# Include the regions for the subservices and the services not present
logging.info("Updating subservices and the services not present in the original matrix")
# accessanalyzer --> iam
regions_by_service["services"]["accessanalyzer"] = regions_by_service["services"]["iam"]
# apigatewayv2 --> apigateway
regions_by_service["services"]["apigatewayv2"] = regions_by_service["services"][
"apigateway"
]
# macie2 --> macie
regions_by_service["services"]["macie2"] = regions_by_service["services"]["macie"]
# logs --> cloudwatch
regions_by_service["services"]["logs"] = regions_by_service["services"]["cloudwatch"]
# dax --> dynamodb
regions_by_service["services"]["dax"] = regions_by_service["services"]["dynamodb"]
# glacier --> s3
regions_by_service["services"]["glacier"] = regions_by_service["services"]["s3"]
# opensearch --> es
regions_by_service["services"]["opensearch"] = regions_by_service["services"]["es"]
# elbv2 --> elb
regions_by_service["services"]["elbv2"] = regions_by_service["services"]["elb"]
# route53domains --> route53
regions_by_service["services"]["route53domains"] = regions_by_service["services"][
"route53"
]
# s3control --> s3
regions_by_service["services"]["s3control"] = regions_by_service["services"]["s3"]
# wafv2 --> waf
regions_by_service["services"]["wafv2"] = regions_by_service["services"]["waf"]
# waf-regional --> waf
regions_by_service["services"]["waf-regional"] = regions_by_service["services"]["waf"]
# Write to file
logging.info(f"Writing {parsed__matrix_regions_aws}")
with open(parsed__matrix_regions_aws, "w") as outfile:
logging.info(f"Writing {parsed_matrix_regions_aws}")
with open(parsed_matrix_regions_aws, "w") as outfile:
json.dump(regions_by_service, outfile, indent=2, sort_keys=True)