1
+ import * as R from 'ramda' ;
2
+ import createEnvironmentVariableRelationships from './createEnvironmentVariableRelationships.mjs' ;
3
+ import logger from '../logger.mjs' ;
4
+ import {
5
+ safeForEach ,
6
+ createAssociatedRelationship ,
7
+ createArn ,
8
+ createAttachedRelationship
9
+ } from '../utils.mjs' ;
10
+ import {
11
+ VPC ,
12
+ EC2 ,
13
+ TRANSIT_GATEWAY_ATTACHMENT ,
14
+ AWS_EC2_TRANSIT_GATEWAY ,
15
+ AWS_EC2_VPC ,
16
+ AWS_EC2_SUBNET ,
17
+ FULFILLED ,
18
+ SUBNET
19
+ } from '../constants.mjs' ;
20
+
21
+ function createBatchedHandlers ( lookUpMaps , awsClient ) {
22
+ const {
23
+ envVarResourceIdentifierToIdMap,
24
+ endpointToIdMap,
25
+ resourceMap
26
+ } = lookUpMaps ;
27
+
28
+ return {
29
+ eventSources : async ( credentials , accountId , region ) => {
30
+ const lambdaClient = awsClient . createLambdaClient ( credentials , region ) ;
31
+ const eventSourceMappings = await lambdaClient . listEventSourceMappings ( ) ;
32
+
33
+ return safeForEach ( ( { EventSourceArn, FunctionArn} ) => {
34
+ if ( resourceMap . has ( EventSourceArn ) && resourceMap . has ( FunctionArn ) ) {
35
+ const { resourceType} = resourceMap . get ( EventSourceArn ) ;
36
+ const lambda = resourceMap . get ( FunctionArn ) ;
37
+
38
+ lambda . relationships . push ( createAssociatedRelationship ( resourceType , {
39
+ arn : EventSourceArn
40
+ } ) ) ;
41
+ }
42
+ } , eventSourceMappings ) ;
43
+ } ,
44
+ functions : async ( credentials , accountId , region ) => {
45
+ const lambdaClient = awsClient . createLambdaClient ( credentials , region ) ;
46
+
47
+ const lambdas = await lambdaClient . getAllFunctions ( ) ;
48
+
49
+ return safeForEach ( ( { FunctionArn, Environment} ) => {
50
+ const lambda = resourceMap . get ( FunctionArn ) ;
51
+ // Environment can be null (not undefined) which means default function parameters can't be used
52
+ const environment = Environment ?? { } ;
53
+ // a lambda may have been created between the time we got the data from config
54
+ // and made our api request
55
+ if ( lambda != null && ! R . isEmpty ( environment ) ) {
56
+ // The lambda API returns an error object if there are encrypted environment variables
57
+ // that the discovery process does not have permissions to decrypt
58
+ if ( R . isNil ( environment . Error ) ) {
59
+ //TODO: add env var name as a property of the edge
60
+ lambda . relationships . push ( ...createEnvironmentVariableRelationships (
61
+ { resourceMap, envVarResourceIdentifierToIdMap, endpointToIdMap} ,
62
+ { accountId, awsRegion : region } ,
63
+ environment . Variables ) ) ;
64
+ }
65
+ }
66
+ } , lambdas ) ;
67
+ } ,
68
+ snsSubscriptions : async ( credentials , accountId , region ) => {
69
+ const snsClient = awsClient . createSnsClient ( credentials , region ) ;
70
+
71
+ const subscriptions = await snsClient . getAllSubscriptions ( ) ;
72
+
73
+ return safeForEach ( ( { Endpoint, TopicArn} ) => {
74
+ // an SNS topic may have been created between the time we got the data from config
75
+ // and made our api request or the endpoint may have been created in a region that
76
+ // has not been imported
77
+ if ( resourceMap . has ( TopicArn ) && resourceMap . has ( Endpoint ) ) {
78
+ const snsTopic = resourceMap . get ( TopicArn ) ;
79
+ const { resourceType} = resourceMap . get ( Endpoint ) ;
80
+ snsTopic . relationships . push ( createAssociatedRelationship ( resourceType , { arn : Endpoint } ) ) ;
81
+ }
82
+ } , subscriptions ) ;
83
+ } ,
84
+ transitGatewayVpcAttachments : async ( credentials , accountId , region ) => {
85
+ // Whilst AWS Config supports the AWS::EC2::TransitGatewayAttachment resource type,
86
+ // it is missing information on the account that VPCs referred to by the attachment
87
+ // are deployed in. Therefore we need to supplement this with info from the EC2 API.
88
+ const ec2Client = awsClient . createEc2Client ( credentials , region ) ;
89
+
90
+ const tgwAttachments = await ec2Client . getAllTransitGatewayAttachments ( [
91
+ { Name : 'resource-type' , Values : [ VPC . toLowerCase ( ) ] }
92
+ ] ) ;
93
+
94
+ return safeForEach ( tgwAttachment => {
95
+ const {
96
+ TransitGatewayAttachmentId, ResourceOwnerId, TransitGatewayOwnerId, TransitGatewayId
97
+ } = tgwAttachment ;
98
+ const tgwAttachmentArn = createArn ( {
99
+ service : EC2 , region, accountId, resource : `${ TRANSIT_GATEWAY_ATTACHMENT } /${ TransitGatewayAttachmentId } ` }
100
+ ) ;
101
+
102
+ if ( resourceMap . has ( tgwAttachmentArn ) ) {
103
+ const tgwAttachmentFromConfig = resourceMap . get ( tgwAttachmentArn ) ;
104
+ const { relationships, configuration : { SubnetIds, VpcId} } = tgwAttachmentFromConfig ;
105
+
106
+ relationships . push (
107
+ createAttachedRelationship ( AWS_EC2_TRANSIT_GATEWAY , { accountId : TransitGatewayOwnerId , awsRegion : region , resourceId : TransitGatewayId } ) ,
108
+ createAssociatedRelationship ( AWS_EC2_VPC , { relNameSuffix : VPC , accountId : ResourceOwnerId , awsRegion : region , resourceId : VpcId } ) ,
109
+ ...SubnetIds . map ( subnetId => createAssociatedRelationship ( AWS_EC2_SUBNET , { relNameSuffix : SUBNET , accountId : ResourceOwnerId , awsRegion : region , resourceId : subnetId } ) )
110
+ ) ;
111
+ }
112
+ } , tgwAttachments ) ;
113
+ }
114
+ }
115
+ }
116
+
117
+ function logErrors ( results ) {
118
+ const errors = results . flatMap ( ( { status, value, reason} ) => {
119
+ if ( status === FULFILLED ) {
120
+ return value . errors ;
121
+ } else {
122
+ return [ { error : reason } ]
123
+ }
124
+ } ) ;
125
+
126
+ logger . error ( `There were ${ errors . length } errors when adding batch additional relationships.` ) ;
127
+ logger . debug ( 'Errors: ' , { errors : errors } ) ;
128
+ }
129
+
130
+ async function addBatchedRelationships ( lookUpMaps , awsClient ) {
131
+ const credentialsTuples = Array . from ( lookUpMaps . accountsMap . entries ( ) ) ;
132
+
133
+ const batchedHandlers = createBatchedHandlers ( lookUpMaps , awsClient ) ;
134
+
135
+ const results = await Promise . allSettled ( Object . values ( batchedHandlers ) . flatMap ( handler => {
136
+ return credentialsTuples
137
+ . flatMap ( ( [ accountId , { regions, credentials} ] ) =>
138
+ regions . map ( region => handler ( credentials , accountId , region ) )
139
+ ) ;
140
+ } ) ) ;
141
+
142
+ logErrors ( results ) ;
143
+ }
144
+
145
+ export default addBatchedRelationships ;
0 commit comments