cartography 0.102.0rc1__py3-none-any.whl → 0.103.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +376 -249
- cartography/client/core/tx.py +39 -18
- cartography/config.py +28 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/cloudwatch.py +93 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +327 -0
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/efs.py +93 -0
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +57 -44
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/cloudflare/__init__.py +74 -0
- cartography/intel/cloudflare/accounts.py +57 -0
- cartography/intel/cloudflare/dnsrecords.py +64 -0
- cartography/intel/cloudflare/members.py +75 -0
- cartography/intel/cloudflare/roles.py +65 -0
- cartography/intel/cloudflare/zones.py +64 -0
- cartography/intel/create_indexes.py +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +36 -10
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +90 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +94 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +78 -0
- cartography/intel/openai/util.py +29 -0
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- cartography/intel/tailscale/__init__.py +77 -0
- cartography/intel/tailscale/acls.py +146 -0
- cartography/intel/tailscale/devices.py +127 -0
- cartography/intel/tailscale/postureintegrations.py +81 -0
- cartography/intel/tailscale/tailnets.py +76 -0
- cartography/intel/tailscale/users.py +80 -0
- cartography/intel/tailscale/utils.py +132 -0
- cartography/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/efs/__init__.py +0 -0
- cartography/models/aws/efs/mount_target.py +52 -0
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/bigfix/bigfix_computer.py +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/cloudflare/__init__.py +0 -0
- cartography/models/cloudflare/account.py +25 -0
- cartography/models/cloudflare/dnsrecord.py +55 -0
- cartography/models/cloudflare/member.py +82 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +56 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/openai/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +90 -0
- cartography/models/openai/apikey.py +84 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +70 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +49 -0
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/models/tailscale/__init__.py +0 -0
- cartography/models/tailscale/device.py +95 -0
- cartography/models/tailscale/group.py +86 -0
- cartography/models/tailscale/postureintegration.py +58 -0
- cartography/models/tailscale/tag.py +102 -0
- cartography/models/tailscale/tailnet.py +29 -0
- cartography/models/tailscale/user.py +52 -0
- cartography/stats.py +3 -3
- cartography/sync.py +113 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
- cartography-0.103.0.dist-info/RECORD +442 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc1.dist-info/RECORD +0 -377
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import boto3
|
|
5
|
+
import neo4j
|
|
6
|
+
|
|
7
|
+
from cartography.client.core.tx import load
|
|
8
|
+
from cartography.graph.job import GraphJob
|
|
9
|
+
from cartography.intel.aws.ec2.util import get_botocore_config
|
|
10
|
+
from cartography.models.aws.ec2.route_table_associations import (
|
|
11
|
+
RouteTableAssociationSchema,
|
|
12
|
+
)
|
|
13
|
+
from cartography.models.aws.ec2.route_tables import RouteTableSchema
|
|
14
|
+
from cartography.models.aws.ec2.routes import RouteSchema
|
|
15
|
+
from cartography.util import aws_handle_regions
|
|
16
|
+
from cartography.util import timeit
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_route_id_and_target(
|
|
22
|
+
route_table_id: str, route: dict[str, Any]
|
|
23
|
+
) -> tuple[str, str | None]:
|
|
24
|
+
"""
|
|
25
|
+
Generate a unique identifier for an AWS EC2 route and return the target of the route
|
|
26
|
+
regardless of its type.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
route_table_id: The ID of the route table this route belongs to
|
|
30
|
+
route: The route data from AWS API
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
A tuple containing the unique identifier for the route and the target of the route
|
|
34
|
+
"""
|
|
35
|
+
route_target_keys = [
|
|
36
|
+
"DestinationCidrBlock",
|
|
37
|
+
"DestinationIpv6CidrBlock",
|
|
38
|
+
"GatewayId",
|
|
39
|
+
"InstanceId",
|
|
40
|
+
"NatGatewayId",
|
|
41
|
+
"TransitGatewayId",
|
|
42
|
+
"LocalGatewayId",
|
|
43
|
+
"CarrierGatewayId",
|
|
44
|
+
"NetworkInterfaceId",
|
|
45
|
+
"VpcPeeringConnectionId",
|
|
46
|
+
"EgressOnlyInternetGatewayId",
|
|
47
|
+
"CoreNetworkArn",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
# Start with the route table ID
|
|
51
|
+
parts = [route_table_id]
|
|
52
|
+
target = None
|
|
53
|
+
found_target = False
|
|
54
|
+
|
|
55
|
+
for key in route_target_keys:
|
|
56
|
+
# Each route is a "union"-like data structure, so only one of the keys will be present.
|
|
57
|
+
if key in route:
|
|
58
|
+
parts.append(route[key])
|
|
59
|
+
target = route[key]
|
|
60
|
+
found_target = True
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
if not found_target:
|
|
64
|
+
logger.warning(
|
|
65
|
+
f"No target found for route in {route_table_id}. Please review the route and file an issue to "
|
|
66
|
+
"https://github.com/cartography-cncf/cartography/issues sharing what the route table looks like "
|
|
67
|
+
"so that we can update the available keys.",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return "|".join(parts), target
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@timeit
|
|
74
|
+
@aws_handle_regions
|
|
75
|
+
def get_route_tables(
|
|
76
|
+
boto3_session: boto3.session.Session, region: str
|
|
77
|
+
) -> list[dict[str, Any]]:
|
|
78
|
+
client = boto3_session.client(
|
|
79
|
+
"ec2", region_name=region, config=get_botocore_config()
|
|
80
|
+
)
|
|
81
|
+
paginator = client.get_paginator("describe_route_tables")
|
|
82
|
+
route_tables: list[dict[str, Any]] = []
|
|
83
|
+
for page in paginator.paginate():
|
|
84
|
+
route_tables.extend(page["RouteTables"])
|
|
85
|
+
return route_tables
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _transform_route_table_associations(
|
|
89
|
+
route_table_id: str,
|
|
90
|
+
associations: list[dict[str, Any]],
|
|
91
|
+
) -> tuple[list[dict[str, Any]], bool]:
|
|
92
|
+
"""
|
|
93
|
+
Transform route table association data into a format suitable for cartography ingestion.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
route_table_id: The ID of the route table
|
|
97
|
+
associations: List of association data from AWS API
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
1. List of transformed association data
|
|
101
|
+
2. Boolean indicating if the association is the main association, meaning that the route table is the main
|
|
102
|
+
route table for the VPC
|
|
103
|
+
"""
|
|
104
|
+
transformed = []
|
|
105
|
+
is_main = False
|
|
106
|
+
for association in associations:
|
|
107
|
+
if association.get("SubnetId"):
|
|
108
|
+
target = association["SubnetId"]
|
|
109
|
+
elif association.get("GatewayId"):
|
|
110
|
+
target = association["GatewayId"]
|
|
111
|
+
else:
|
|
112
|
+
is_main = True
|
|
113
|
+
target = "main"
|
|
114
|
+
|
|
115
|
+
transformed_association = {
|
|
116
|
+
"id": association["RouteTableAssociationId"],
|
|
117
|
+
"route_table_id": route_table_id,
|
|
118
|
+
"subnet_id": association.get("SubnetId"),
|
|
119
|
+
"gateway_id": association.get("GatewayId"),
|
|
120
|
+
"main": association.get("Main", False),
|
|
121
|
+
"association_state": association.get("AssociationState", {}).get("State"),
|
|
122
|
+
"association_state_message": association.get("AssociationState", {}).get(
|
|
123
|
+
"Message"
|
|
124
|
+
),
|
|
125
|
+
"_target": target,
|
|
126
|
+
}
|
|
127
|
+
transformed.append(transformed_association)
|
|
128
|
+
return transformed, is_main
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _transform_route_table_routes(
|
|
132
|
+
route_table_id: str, routes: list[dict[str, Any]]
|
|
133
|
+
) -> list[dict[str, Any]]:
|
|
134
|
+
"""
|
|
135
|
+
Transform route table route data into a format suitable for cartography ingestion.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
route_table_id: The ID of the route table
|
|
139
|
+
routes: List of route data from AWS API
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of transformed route data
|
|
143
|
+
"""
|
|
144
|
+
transformed = []
|
|
145
|
+
for route in routes:
|
|
146
|
+
route_id, target = _get_route_id_and_target(route_table_id, route)
|
|
147
|
+
|
|
148
|
+
transformed_route = {
|
|
149
|
+
"id": route_id,
|
|
150
|
+
"route_table_id": route_table_id,
|
|
151
|
+
"destination_cidr_block": route.get("DestinationCidrBlock"),
|
|
152
|
+
"destination_ipv6_cidr_block": route.get("DestinationIpv6CidrBlock"),
|
|
153
|
+
"gateway_id": route.get("GatewayId"),
|
|
154
|
+
"instance_id": route.get("InstanceId"),
|
|
155
|
+
"instance_owner_id": route.get("InstanceOwnerId"),
|
|
156
|
+
"nat_gateway_id": route.get("NatGatewayId"),
|
|
157
|
+
"transit_gateway_id": route.get("TransitGatewayId"),
|
|
158
|
+
"local_gateway_id": route.get("LocalGatewayId"),
|
|
159
|
+
"carrier_gateway_id": route.get("CarrierGatewayId"),
|
|
160
|
+
"network_interface_id": route.get("NetworkInterfaceId"),
|
|
161
|
+
"vpc_peering_connection_id": route.get("VpcPeeringConnectionId"),
|
|
162
|
+
"state": route.get("State"),
|
|
163
|
+
"origin": route.get("Origin"),
|
|
164
|
+
"core_network_arn": route.get("CoreNetworkArn"),
|
|
165
|
+
"destination_prefix_list_id": route.get("DestinationPrefixListId"),
|
|
166
|
+
"egress_only_internet_gateway_id": route.get("EgressOnlyInternetGatewayId"),
|
|
167
|
+
"_target": target,
|
|
168
|
+
}
|
|
169
|
+
transformed.append(transformed_route)
|
|
170
|
+
return transformed
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def transform_route_table_data(
|
|
174
|
+
route_tables: list[dict[str, Any]],
|
|
175
|
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], list[dict[str, Any]]]:
|
|
176
|
+
"""
|
|
177
|
+
Transform route table data into a format suitable for cartography ingestion.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
route_tables: List of route table data from AWS API
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Tuple of (transformed route table data, transformed association data, transformed route data)
|
|
184
|
+
"""
|
|
185
|
+
transformed_tables = []
|
|
186
|
+
association_data = []
|
|
187
|
+
route_data = []
|
|
188
|
+
|
|
189
|
+
for rt in route_tables:
|
|
190
|
+
route_table_id = rt["RouteTableId"]
|
|
191
|
+
|
|
192
|
+
# Transform routes
|
|
193
|
+
current_routes = []
|
|
194
|
+
if rt.get("Routes"):
|
|
195
|
+
current_routes = _transform_route_table_routes(route_table_id, rt["Routes"])
|
|
196
|
+
route_data.extend(current_routes)
|
|
197
|
+
|
|
198
|
+
# If the rt has a association marked with main=True, then it is the main route table for the VPC.
|
|
199
|
+
is_main = False
|
|
200
|
+
# Transform associations
|
|
201
|
+
if rt.get("Associations"):
|
|
202
|
+
associations, is_main = _transform_route_table_associations(
|
|
203
|
+
route_table_id, rt["Associations"]
|
|
204
|
+
)
|
|
205
|
+
association_data.extend(associations)
|
|
206
|
+
|
|
207
|
+
transformed_rt = {
|
|
208
|
+
"id": route_table_id,
|
|
209
|
+
"route_table_id": route_table_id,
|
|
210
|
+
"owner_id": rt.get("OwnerId"),
|
|
211
|
+
"vpc_id": rt.get("VpcId"),
|
|
212
|
+
"VpnGatewayIds": [
|
|
213
|
+
vgw["GatewayId"] for vgw in rt.get("PropagatingVgws", [])
|
|
214
|
+
],
|
|
215
|
+
"RouteTableAssociationIds": [
|
|
216
|
+
assoc["RouteTableAssociationId"] for assoc in rt.get("Associations", [])
|
|
217
|
+
],
|
|
218
|
+
"RouteIds": [route["id"] for route in current_routes],
|
|
219
|
+
"tags": rt.get("Tags", []),
|
|
220
|
+
"main": is_main,
|
|
221
|
+
}
|
|
222
|
+
transformed_tables.append(transformed_rt)
|
|
223
|
+
|
|
224
|
+
return transformed_tables, association_data, route_data
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@timeit
|
|
228
|
+
def load_route_tables(
|
|
229
|
+
neo4j_session: neo4j.Session,
|
|
230
|
+
data: list[dict[str, Any]],
|
|
231
|
+
region: str,
|
|
232
|
+
current_aws_account_id: str,
|
|
233
|
+
update_tag: int,
|
|
234
|
+
) -> None:
|
|
235
|
+
load(
|
|
236
|
+
neo4j_session,
|
|
237
|
+
RouteTableSchema(),
|
|
238
|
+
data,
|
|
239
|
+
Region=region,
|
|
240
|
+
AWS_ID=current_aws_account_id,
|
|
241
|
+
lastupdated=update_tag,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@timeit
|
|
246
|
+
def load_route_table_associations(
|
|
247
|
+
neo4j_session: neo4j.Session,
|
|
248
|
+
data: list[dict[str, Any]],
|
|
249
|
+
region: str,
|
|
250
|
+
current_aws_account_id: str,
|
|
251
|
+
update_tag: int,
|
|
252
|
+
) -> None:
|
|
253
|
+
load(
|
|
254
|
+
neo4j_session,
|
|
255
|
+
RouteTableAssociationSchema(),
|
|
256
|
+
data,
|
|
257
|
+
Region=region,
|
|
258
|
+
AWS_ID=current_aws_account_id,
|
|
259
|
+
lastupdated=update_tag,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@timeit
|
|
264
|
+
def load_routes(
|
|
265
|
+
neo4j_session: neo4j.Session,
|
|
266
|
+
data: list[dict[str, Any]],
|
|
267
|
+
region: str,
|
|
268
|
+
current_aws_account_id: str,
|
|
269
|
+
update_tag: int,
|
|
270
|
+
) -> None:
|
|
271
|
+
load(
|
|
272
|
+
neo4j_session,
|
|
273
|
+
RouteSchema(),
|
|
274
|
+
data,
|
|
275
|
+
Region=region,
|
|
276
|
+
AWS_ID=current_aws_account_id,
|
|
277
|
+
lastupdated=update_tag,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@timeit
|
|
282
|
+
def cleanup(
|
|
283
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
284
|
+
) -> None:
|
|
285
|
+
logger.debug("Running EC2 route tables cleanup")
|
|
286
|
+
GraphJob.from_node_schema(RouteTableSchema(), common_job_parameters).run(
|
|
287
|
+
neo4j_session
|
|
288
|
+
)
|
|
289
|
+
GraphJob.from_node_schema(RouteSchema(), common_job_parameters).run(neo4j_session)
|
|
290
|
+
GraphJob.from_node_schema(RouteTableAssociationSchema(), common_job_parameters).run(
|
|
291
|
+
neo4j_session
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@timeit
|
|
296
|
+
def sync_route_tables(
|
|
297
|
+
neo4j_session: neo4j.Session,
|
|
298
|
+
boto3_session: boto3.session.Session,
|
|
299
|
+
regions: list[str],
|
|
300
|
+
current_aws_account_id: str,
|
|
301
|
+
update_tag: int,
|
|
302
|
+
common_job_parameters: dict[str, Any],
|
|
303
|
+
) -> None:
|
|
304
|
+
for region in regions:
|
|
305
|
+
logger.info(
|
|
306
|
+
"Syncing EC2 route tables for region '%s' in account '%s'.",
|
|
307
|
+
region,
|
|
308
|
+
current_aws_account_id,
|
|
309
|
+
)
|
|
310
|
+
route_tables = get_route_tables(boto3_session, region)
|
|
311
|
+
transformed_tables, association_data, route_data = transform_route_table_data(
|
|
312
|
+
route_tables
|
|
313
|
+
)
|
|
314
|
+
load_routes(
|
|
315
|
+
neo4j_session, route_data, region, current_aws_account_id, update_tag
|
|
316
|
+
)
|
|
317
|
+
load_route_table_associations(
|
|
318
|
+
neo4j_session, association_data, region, current_aws_account_id, update_tag
|
|
319
|
+
)
|
|
320
|
+
load_route_tables(
|
|
321
|
+
neo4j_session,
|
|
322
|
+
transformed_tables,
|
|
323
|
+
region,
|
|
324
|
+
current_aws_account_id,
|
|
325
|
+
update_tag,
|
|
326
|
+
)
|
|
327
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
@@ -6,30 +6,46 @@ from typing import List
|
|
|
6
6
|
import boto3
|
|
7
7
|
import neo4j
|
|
8
8
|
|
|
9
|
-
from .util import get_botocore_config
|
|
10
9
|
from cartography.graph.job import GraphJob
|
|
11
|
-
from cartography.models.aws.ec2.securitygroup_instance import
|
|
10
|
+
from cartography.models.aws.ec2.securitygroup_instance import (
|
|
11
|
+
EC2SecurityGroupInstanceSchema,
|
|
12
|
+
)
|
|
12
13
|
from cartography.util import aws_handle_regions
|
|
13
14
|
from cartography.util import run_cleanup_job
|
|
14
15
|
from cartography.util import timeit
|
|
15
16
|
|
|
17
|
+
from .util import get_botocore_config
|
|
18
|
+
|
|
16
19
|
logger = logging.getLogger(__name__)
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@timeit
|
|
20
23
|
@aws_handle_regions
|
|
21
|
-
def get_ec2_security_group_data(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
def get_ec2_security_group_data(
|
|
25
|
+
boto3_session: boto3.session.Session,
|
|
26
|
+
region: str,
|
|
27
|
+
) -> List[Dict]:
|
|
28
|
+
client = boto3_session.client(
|
|
29
|
+
"ec2",
|
|
30
|
+
region_name=region,
|
|
31
|
+
config=get_botocore_config(),
|
|
32
|
+
)
|
|
33
|
+
paginator = client.get_paginator("describe_security_groups")
|
|
24
34
|
security_groups: List[Dict] = []
|
|
25
35
|
for page in paginator.paginate():
|
|
26
|
-
security_groups.extend(page[
|
|
36
|
+
security_groups.extend(page["SecurityGroups"])
|
|
27
37
|
return security_groups
|
|
28
38
|
|
|
29
39
|
|
|
30
40
|
@timeit
|
|
31
|
-
def load_ec2_security_group_rule(
|
|
32
|
-
|
|
41
|
+
def load_ec2_security_group_rule(
|
|
42
|
+
neo4j_session: neo4j.Session,
|
|
43
|
+
group: Dict,
|
|
44
|
+
rule_type: str,
|
|
45
|
+
update_tag: int,
|
|
46
|
+
) -> None:
|
|
47
|
+
INGEST_RULE_TEMPLATE = Template(
|
|
48
|
+
"""
|
|
33
49
|
MERGE (rule:$rule_label{ruleid: $RuleId})
|
|
34
50
|
ON CREATE SET rule :IpRule, rule.firstseen = timestamp(), rule.fromport = $FromPort, rule.toport = $ToPort,
|
|
35
51
|
rule.protocol = $Protocol
|
|
@@ -39,7 +55,8 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
|
|
|
39
55
|
MERGE (group)<-[r:MEMBER_OF_EC2_SECURITY_GROUP]-(rule)
|
|
40
56
|
ON CREATE SET r.firstseen = timestamp()
|
|
41
57
|
SET r.lastupdated = $update_tag;
|
|
42
|
-
"""
|
|
58
|
+
""",
|
|
59
|
+
)
|
|
43
60
|
|
|
44
61
|
ingest_rule_group_pair = """
|
|
45
62
|
MERGE (group:EC2SecurityGroup{id: $GroupId})
|
|
@@ -64,7 +81,10 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
|
|
|
64
81
|
"""
|
|
65
82
|
|
|
66
83
|
group_id = group["GroupId"]
|
|
67
|
-
rule_type_map = {
|
|
84
|
+
rule_type_map = {
|
|
85
|
+
"IpPermissions": "IpPermissionInbound",
|
|
86
|
+
"IpPermissionsEgress": "IpPermissionEgress",
|
|
87
|
+
}
|
|
68
88
|
|
|
69
89
|
if group.get(rule_type):
|
|
70
90
|
for rule in group[rule_type]:
|
|
@@ -76,7 +96,9 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
|
|
|
76
96
|
# NOTE Cypher query syntax is incompatible with Python string formatting, so we have to do this awkward
|
|
77
97
|
# NOTE manual formatting instead.
|
|
78
98
|
neo4j_session.run(
|
|
79
|
-
INGEST_RULE_TEMPLATE.safe_substitute(
|
|
99
|
+
INGEST_RULE_TEMPLATE.safe_substitute(
|
|
100
|
+
rule_label=rule_type_map[rule_type],
|
|
101
|
+
),
|
|
80
102
|
RuleId=ruleid,
|
|
81
103
|
FromPort=from_port,
|
|
82
104
|
ToPort=to_port,
|
|
@@ -104,8 +126,11 @@ def load_ec2_security_group_rule(neo4j_session: neo4j.Session, group: Dict, rule
|
|
|
104
126
|
|
|
105
127
|
@timeit
|
|
106
128
|
def load_ec2_security_groupinfo(
|
|
107
|
-
neo4j_session: neo4j.Session,
|
|
108
|
-
|
|
129
|
+
neo4j_session: neo4j.Session,
|
|
130
|
+
data: List[Dict],
|
|
131
|
+
region: str,
|
|
132
|
+
current_aws_account_id: str,
|
|
133
|
+
update_tag: int,
|
|
109
134
|
) -> None:
|
|
110
135
|
ingest_security_group = """
|
|
111
136
|
MERGE (group:EC2SecurityGroup{id: $GroupId})
|
|
@@ -138,26 +163,51 @@ def load_ec2_security_groupinfo(
|
|
|
138
163
|
)
|
|
139
164
|
|
|
140
165
|
load_ec2_security_group_rule(neo4j_session, group, "IpPermissions", update_tag)
|
|
141
|
-
load_ec2_security_group_rule(
|
|
166
|
+
load_ec2_security_group_rule(
|
|
167
|
+
neo4j_session,
|
|
168
|
+
group,
|
|
169
|
+
"IpPermissionsEgress",
|
|
170
|
+
update_tag,
|
|
171
|
+
)
|
|
142
172
|
|
|
143
173
|
|
|
144
174
|
@timeit
|
|
145
|
-
def cleanup_ec2_security_groupinfo(
|
|
175
|
+
def cleanup_ec2_security_groupinfo(
|
|
176
|
+
neo4j_session: neo4j.Session,
|
|
177
|
+
common_job_parameters: Dict,
|
|
178
|
+
) -> None:
|
|
146
179
|
run_cleanup_job(
|
|
147
|
-
|
|
180
|
+
"aws_import_ec2_security_groupinfo_cleanup.json",
|
|
148
181
|
neo4j_session,
|
|
149
182
|
common_job_parameters,
|
|
150
183
|
)
|
|
151
|
-
GraphJob.from_node_schema(
|
|
184
|
+
GraphJob.from_node_schema(
|
|
185
|
+
EC2SecurityGroupInstanceSchema(),
|
|
186
|
+
common_job_parameters,
|
|
187
|
+
).run(neo4j_session)
|
|
152
188
|
|
|
153
189
|
|
|
154
190
|
@timeit
|
|
155
191
|
def sync_ec2_security_groupinfo(
|
|
156
|
-
neo4j_session: neo4j.Session,
|
|
157
|
-
|
|
192
|
+
neo4j_session: neo4j.Session,
|
|
193
|
+
boto3_session: boto3.session.Session,
|
|
194
|
+
regions: List[str],
|
|
195
|
+
current_aws_account_id: str,
|
|
196
|
+
update_tag: int,
|
|
197
|
+
common_job_parameters: Dict,
|
|
158
198
|
) -> None:
|
|
159
199
|
for region in regions:
|
|
160
|
-
logger.info(
|
|
200
|
+
logger.info(
|
|
201
|
+
"Syncing EC2 security groups for region '%s' in account '%s'.",
|
|
202
|
+
region,
|
|
203
|
+
current_aws_account_id,
|
|
204
|
+
)
|
|
161
205
|
data = get_ec2_security_group_data(boto3_session, region)
|
|
162
|
-
load_ec2_security_groupinfo(
|
|
206
|
+
load_ec2_security_groupinfo(
|
|
207
|
+
neo4j_session,
|
|
208
|
+
data,
|
|
209
|
+
region,
|
|
210
|
+
current_aws_account_id,
|
|
211
|
+
update_tag,
|
|
212
|
+
)
|
|
163
213
|
cleanup_ec2_security_groupinfo(neo4j_session, common_job_parameters)
|
|
@@ -14,34 +14,46 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
@timeit
|
|
17
|
-
def get_snapshots_in_use(
|
|
17
|
+
def get_snapshots_in_use(
|
|
18
|
+
neo4j_session: neo4j.Session,
|
|
19
|
+
region: str,
|
|
20
|
+
current_aws_account_id: str,
|
|
21
|
+
) -> List[str]:
|
|
18
22
|
query = """
|
|
19
23
|
MATCH (:AWSAccount{id: $AWS_ACCOUNT_ID})-[:RESOURCE]->(v:EBSVolume)
|
|
20
24
|
WHERE v.region = $Region
|
|
21
25
|
RETURN v.snapshotid as snapshot
|
|
22
26
|
"""
|
|
23
|
-
results = neo4j_session.run(
|
|
24
|
-
|
|
27
|
+
results = neo4j_session.run(
|
|
28
|
+
query,
|
|
29
|
+
AWS_ACCOUNT_ID=current_aws_account_id,
|
|
30
|
+
Region=region,
|
|
31
|
+
)
|
|
32
|
+
return [r["snapshot"] for r in results if r["snapshot"]]
|
|
25
33
|
|
|
26
34
|
|
|
27
35
|
@timeit
|
|
28
36
|
@aws_handle_regions
|
|
29
|
-
def get_snapshots(
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
def get_snapshots(
|
|
38
|
+
boto3_session: boto3.session.Session,
|
|
39
|
+
region: str,
|
|
40
|
+
in_use_snapshot_ids: List[str],
|
|
41
|
+
) -> List[Dict]:
|
|
42
|
+
client = boto3_session.client("ec2", region_name=region)
|
|
43
|
+
paginator = client.get_paginator("describe_snapshots")
|
|
32
44
|
snapshots: List[Dict] = []
|
|
33
|
-
for page in paginator.paginate(OwnerIds=[
|
|
34
|
-
snapshots.extend(page[
|
|
45
|
+
for page in paginator.paginate(OwnerIds=["self"]):
|
|
46
|
+
snapshots.extend(page["Snapshots"])
|
|
35
47
|
|
|
36
48
|
# fetch in-use snapshots not in self_owned snapshots
|
|
37
|
-
self_owned_snapshot_ids = {s[
|
|
49
|
+
self_owned_snapshot_ids = {s["SnapshotId"] for s in snapshots}
|
|
38
50
|
other_snapshot_ids = set(in_use_snapshot_ids) - self_owned_snapshot_ids
|
|
39
51
|
if other_snapshot_ids:
|
|
40
52
|
try:
|
|
41
53
|
for page in paginator.paginate(SnapshotIds=list(other_snapshot_ids)):
|
|
42
|
-
snapshots.extend(page[
|
|
54
|
+
snapshots.extend(page["Snapshots"])
|
|
43
55
|
except ClientError as e:
|
|
44
|
-
if e.response[
|
|
56
|
+
if e.response["Error"]["Code"] == "InvalidSnapshot.NotFound":
|
|
45
57
|
logger.warning(
|
|
46
58
|
f"Failed to retrieve page of in-use, \
|
|
47
59
|
not owned snapshots. Continuing anyway. Error - {e}",
|
|
@@ -54,7 +66,11 @@ def get_snapshots(boto3_session: boto3.session.Session, region: str, in_use_snap
|
|
|
54
66
|
|
|
55
67
|
@timeit
|
|
56
68
|
def load_snapshots(
|
|
57
|
-
|
|
69
|
+
neo4j_session: neo4j.Session,
|
|
70
|
+
data: List[Dict],
|
|
71
|
+
region: str,
|
|
72
|
+
current_aws_account_id: str,
|
|
73
|
+
update_tag: int,
|
|
58
74
|
) -> None:
|
|
59
75
|
ingest_snapshots = """
|
|
60
76
|
UNWIND $snapshots_list as snapshot
|
|
@@ -73,7 +89,7 @@ def load_snapshots(
|
|
|
73
89
|
"""
|
|
74
90
|
|
|
75
91
|
for snapshot in data:
|
|
76
|
-
snapshot[
|
|
92
|
+
snapshot["StartTime"] = str(snapshot["StartTime"])
|
|
77
93
|
|
|
78
94
|
neo4j_session.run(
|
|
79
95
|
ingest_snapshots,
|
|
@@ -88,7 +104,7 @@ def load_snapshots(
|
|
|
88
104
|
def get_snapshot_volumes(snapshots: List[Dict]) -> List[Dict]:
|
|
89
105
|
snapshot_volumes: List[Dict] = []
|
|
90
106
|
for snapshot in snapshots:
|
|
91
|
-
if snapshot.get(
|
|
107
|
+
if snapshot.get("VolumeId"):
|
|
92
108
|
snapshot_volumes.append(snapshot)
|
|
93
109
|
|
|
94
110
|
return snapshot_volumes
|
|
@@ -96,7 +112,10 @@ def get_snapshot_volumes(snapshots: List[Dict]) -> List[Dict]:
|
|
|
96
112
|
|
|
97
113
|
@timeit
|
|
98
114
|
def load_snapshot_volume_relations(
|
|
99
|
-
|
|
115
|
+
neo4j_session: neo4j.Session,
|
|
116
|
+
data: List[Dict],
|
|
117
|
+
current_aws_account_id: str,
|
|
118
|
+
update_tag: int,
|
|
100
119
|
) -> None:
|
|
101
120
|
ingest_volumes = """
|
|
102
121
|
UNWIND $snapshot_volumes_list as volume
|
|
@@ -124,9 +143,12 @@ def load_snapshot_volume_relations(
|
|
|
124
143
|
|
|
125
144
|
|
|
126
145
|
@timeit
|
|
127
|
-
def cleanup_snapshots(
|
|
146
|
+
def cleanup_snapshots(
|
|
147
|
+
neo4j_session: neo4j.Session,
|
|
148
|
+
common_job_parameters: Dict,
|
|
149
|
+
) -> None:
|
|
128
150
|
run_cleanup_job(
|
|
129
|
-
|
|
151
|
+
"aws_import_snapshots_cleanup.json",
|
|
130
152
|
neo4j_session,
|
|
131
153
|
common_job_parameters,
|
|
132
154
|
)
|
|
@@ -134,14 +156,31 @@ def cleanup_snapshots(neo4j_session: neo4j.Session, common_job_parameters: Dict)
|
|
|
134
156
|
|
|
135
157
|
@timeit
|
|
136
158
|
def sync_ebs_snapshots(
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
neo4j_session: neo4j.Session,
|
|
160
|
+
boto3_session: boto3.session.Session,
|
|
161
|
+
regions: List[str],
|
|
162
|
+
current_aws_account_id: str,
|
|
163
|
+
update_tag: int,
|
|
164
|
+
common_job_parameters: Dict,
|
|
139
165
|
) -> None:
|
|
140
166
|
for region in regions:
|
|
141
|
-
logger.debug(
|
|
142
|
-
|
|
167
|
+
logger.debug(
|
|
168
|
+
"Syncing snapshots for region '%s' in account '%s'.",
|
|
169
|
+
region,
|
|
170
|
+
current_aws_account_id,
|
|
171
|
+
)
|
|
172
|
+
snapshots_in_use = get_snapshots_in_use(
|
|
173
|
+
neo4j_session,
|
|
174
|
+
region,
|
|
175
|
+
current_aws_account_id,
|
|
176
|
+
)
|
|
143
177
|
data = get_snapshots(boto3_session, region, snapshots_in_use)
|
|
144
178
|
load_snapshots(neo4j_session, data, region, current_aws_account_id, update_tag)
|
|
145
179
|
snapshot_volumes = get_snapshot_volumes(data)
|
|
146
|
-
load_snapshot_volume_relations(
|
|
180
|
+
load_snapshot_volume_relations(
|
|
181
|
+
neo4j_session,
|
|
182
|
+
snapshot_volumes,
|
|
183
|
+
current_aws_account_id,
|
|
184
|
+
update_tag,
|
|
185
|
+
)
|
|
147
186
|
cleanup_snapshots(neo4j_session, common_job_parameters)
|