Skip to content

Commit 22a8cfd

Browse files
committed
feat(s3): Add connections to iamRole, lambda, sns and sqs services
1 parent d79a230 commit 22a8cfd

13 files changed

Lines changed: 346 additions & 23 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,14 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
122122
| iamServerCertificate | |
123123
| iamUser | iamGroup |
124124
| iamPolicy | iamRole, iamGroup |
125-
| iamRole | codebuild, configurationRecorder, iamInstanceProfile, iamPolicy, eksCluster, ecsService, flowLog, glueJob, managedAirflow, sageMakerNotebookInstance, systemsManagerInstance guardDutyDetector |
125+
| iamRole | codebuild, configurationRecorder, iamInstanceProfile, iamPolicy, eksCluster, ecsService, flowLog, glueJob, managedAirflow, s3, sageMakerNotebookInstance, systemsManagerInstance guardDutyDetector |
126126
| iamGroup | iamUser, iamPolicy |
127127
| igw | vpc |
128128
| iot | |
129129
| kinesisFirehose | kinesisStream, s3 |
130130
| kinesisStream | kinesisFirehose |
131131
| kms | cloudtrail, cloudwatchLog, codebuild, efs, eksCluster, elastiCacheReplicationGroup, elasticSearchDomain, emrCluster, lambda, rdsClusterSnapshot, sns, sageMakerNotebookInstance, dmsReplicationInstance, redshiftCluster |
132-
| lambda | appSync, cognitoUserPool, kms, securityGroup, subnet, vpc |
132+
| lambda | appSync, cognitoUserPool, kms, s3, securityGroup, subnet, vpc |
133133
| managedAirflow | iamRole, securityGroups, subnet, s3 |
134134
| nacl | vpc |
135135
| natGateway | networkInterface, subnet, vpc |
@@ -145,12 +145,12 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
145145
| sageMakerExperiment | |
146146
| sageMakerNotebookInstance | iamRole, kms, networkInterface, subnet, securityGroup |
147147
| sageMakerProject | |
148-
| s3 | cloudfront, cloudtrail, kinesisFirehose, managedAirflow |
148+
| s3 | cloudfront, cloudtrail, iamRole, kinesisFirehose, lambda, managedAirflow, sns, sqs |
149149
| secretsManager | |
150150
| securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance |
151151
| ses | |
152-
| sns | kms, cloudtrail, cloudwatch |
153-
| sqs | |
152+
| sns | kms, cloudtrail, cloudwatch, s3 |
153+
| sqs | s3 |
154154
| subnet | alb, asg, codebuild, dmsReplicationInstance, ec2, ecsService, efsMountTarget, elastiCacheCluster, elasticSearchDomain, elb, lambda, managedAirflow, natGateway, networkInterface, sageMakerNotebookInstance, routeTable, vpc, eksCluster, emrCluster, flowLog |
155155
| systemsManagerInstance | ec2, iamRole |
156156
| systemsManagerDocument | |

src/services/cloudfront/schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type awsCloudfront @key(fields: "id") {
2121
origins: [awsCloudfrontOriginData]
2222
logging: awsCloudfrontLoggingConfig
2323
elb: [awsElb] @hasInverse(field: cloudfrontDistribution)
24-
s3: [awsS3] @hasInverse(field: cloudfrontDistribution)
24+
s3: [awsS3] @hasInverse(field: cloudfrontDistributions)
2525
tags: [awsRawTag]
2626
webAcl: [awsWafV2WebAcl] @hasInverse(field: cloudfront)
2727
}

src/services/cloudtrail/schema.graphql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type awsCloudtrail @key(fields: "cgId") {
2020
status: awsCloudtrailStatus
2121
eventSelectors: [awsCloudtrailEventSelector]
2222
tags: [awsRawTag]
23-
s3: [awsS3] @hasInverse(field: cloudtrail)
23+
s3: [awsS3] @hasInverse(field: cloudtrails)
2424
sns: [awsSns] @hasInverse(field: cloudtrail)
2525
kms: [awsKms] @hasInverse(field: cloudtrail)
2626
cloudwatchLog: [awsCloudwatchLog] @hasInverse(field: cloudtrail)

src/services/iamRole/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ type awsIamRole @key(fields: "id") {
2323
sageMakerNotebookInstances: [awsSageMakerNotebookInstance] @hasInverse(field: iamRole)
2424
systemsManagerInstances: [awsSystemsManagerInstance] @hasInverse(field: iamRole)
2525
iamInstanceProfiles: [awsIamInstanceProfile] @hasInverse(field: iamRole)
26+
s3: [awsS3] @hasInverse(field: iamRole)
2627
}

src/services/lambda/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type awsLambda @key(fields: "arn") {
2626
vpc: [awsVpc] @hasInverse(field: lambda)
2727
cognitoUserPool: [awsCognitoUserPool] @hasInverse(field: lambda) #change to plural
2828
appSync: [awsAppSync] @hasInverse(field: lambda)
29+
s3: [awsS3] @hasInverse(field: lambdas)
2930
}
3031

3132
type awsLambdaEnvironmentVariable

src/services/s3/connections.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import { ServiceConnection } from '@cloudgraph/sdk'
2+
import isEmpty from 'lodash/isEmpty'
3+
import services from '../../enums/services'
4+
import { RawAwsS3 } from './data'
5+
import { RawAwsIamRole } from '../iamRole/data'
6+
import { RawAwsLambdaFunction } from '../lambda/data'
7+
import { RawAwsSns } from '../sns/data'
8+
import { AwsSqs } from '../sqs/data'
9+
import { globalRegionName } from '../../enums/regions'
10+
11+
/**
12+
* S3
13+
*/
14+
15+
export default ({
16+
service,
17+
data,
18+
region,
19+
}: {
20+
data: { name: string; data: { [property: string]: any[] } }[]
21+
service: RawAwsS3
22+
region: string
23+
}): { [key: string]: ServiceConnection[] } => {
24+
const connections: ServiceConnection[] = []
25+
26+
const {
27+
Id: id,
28+
AdditionalInfo: {
29+
ReplicationConfig: replicationConfig,
30+
NotificationConfiguration: {
31+
LambdaFunctionConfigurations: lambdaFunctionConfigurations,
32+
TopicConfigurations: topicConfigurations,
33+
QueueConfigurations: queueConfigurations,
34+
},
35+
},
36+
} = service
37+
38+
/**
39+
* Find IAM Roles
40+
* related to this S3
41+
*/
42+
const roles: { name: string; data: { [property: string]: any[] } } =
43+
data.find(({ name }) => name === services.iamRole)
44+
if (replicationConfig?.Role && roles?.data?.[globalRegionName]) {
45+
const dataAtRegion: RawAwsIamRole[] = roles.data[globalRegionName].filter(
46+
role => role.Arn === replicationConfig.Role
47+
)
48+
if (!isEmpty(dataAtRegion)) {
49+
for (const instance of dataAtRegion) {
50+
const { Arn: arn }: RawAwsIamRole = instance
51+
52+
connections.push({
53+
id: arn,
54+
resourceType: services.iamRole,
55+
relation: 'child',
56+
field: 'iamRole',
57+
})
58+
}
59+
}
60+
}
61+
62+
/**
63+
* Find lambda functions
64+
* related to this S3
65+
*/
66+
const lambdaFunctions: {
67+
name: string
68+
data: { [property: string]: any[] }
69+
} = data.find(({ name }) => name === services.lambda)
70+
71+
const functionArns = lambdaFunctionConfigurations?.map(
72+
lambdaConfig => lambdaConfig?.LambdaFunctionArn
73+
)
74+
75+
if (!isEmpty(functionArns) && lambdaFunctions?.data?.[region]) {
76+
const dataAtRegion: RawAwsLambdaFunction[] = lambdaFunctions.data[
77+
region
78+
].filter(({ FunctionArn }: RawAwsLambdaFunction) =>
79+
functionArns.includes(FunctionArn)
80+
)
81+
82+
if (!isEmpty(dataAtRegion)) {
83+
for (const lambdaFunction of dataAtRegion) {
84+
const { FunctionArn: functionArn }: RawAwsLambdaFunction =
85+
lambdaFunction
86+
connections.push({
87+
id: functionArn,
88+
resourceType: services.lambda,
89+
relation: 'child',
90+
field: 'lambdas',
91+
})
92+
}
93+
}
94+
}
95+
96+
/**
97+
* Find SNS topic
98+
* related to this S3
99+
*/
100+
const snsTopics = data.find(({ name }) => name === services.sns)
101+
const topicArns = topicConfigurations?.map(topic => topic?.TopicArn)
102+
if (!isEmpty(topicArns) && snsTopics?.data?.[region]) {
103+
const snsTopicsInRegion: RawAwsSns[] = snsTopics.data[region].filter(
104+
({ TopicArn: topicArn }: RawAwsSns) => topicArns.includes(topicArn)
105+
)
106+
107+
if (!isEmpty(snsTopicsInRegion)) {
108+
for (const topic of snsTopicsInRegion) {
109+
const { TopicArn: topicArn }: RawAwsSns = topic
110+
connections.push({
111+
id: topicArn,
112+
resourceType: services.sns,
113+
relation: 'child',
114+
field: 'sns',
115+
})
116+
}
117+
}
118+
}
119+
120+
/**
121+
* Find SQS
122+
* related to this S3
123+
*/
124+
const sqsQueues = data.find(({ name }) => name === services.sqs)
125+
const sqsArns = queueConfigurations?.map(queue => queue?.QueueArn)
126+
if (!isEmpty(sqsArns) && sqsQueues?.data?.[region]) {
127+
const dataAtRegion: AwsSqs[] = sqsQueues.data[region].filter(
128+
({ sqsAttributes: { QueueArn: queueArn } }: AwsSqs) =>
129+
sqsArns.includes(queueArn)
130+
)
131+
if (!isEmpty(dataAtRegion)) {
132+
for (const instance of dataAtRegion) {
133+
const {
134+
sqsAttributes: { QueueArn: queueArn },
135+
}: AwsSqs = instance
136+
137+
connections.push({
138+
id: queueArn,
139+
resourceType: services.sqs,
140+
relation: 'child',
141+
field: 'sqs',
142+
})
143+
}
144+
}
145+
}
146+
147+
const cfStackResult = {
148+
[id]: connections,
149+
}
150+
return cfStackResult
151+
}

src/services/s3/data.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import S3, {
3030
ListBucketsOutput,
3131
ListObjectsV2Output,
3232
LoggingEnabled,
33+
NotificationConfiguration,
3334
Object as S3Object,
3435
Owner,
3536
Payer,
@@ -301,6 +302,29 @@ const getBucketWebsite = async (s3: S3, name: BucketName) =>
301302
)
302303
})
303304

305+
const getBucketNotificationConfiguration = async (s3: S3, name: BucketName) =>
306+
new Promise<NotificationConfiguration | any>(resolve => {
307+
s3.getBucketNotificationConfiguration(
308+
{
309+
Bucket: name,
310+
},
311+
(err: AWSError, data: NotificationConfiguration) => {
312+
if (err) {
313+
errorLog.generateAwsErrorLog({
314+
functionName: 's3:getBucketNotificationConfiguration',
315+
err,
316+
})
317+
}
318+
319+
if (!isEmpty(data)) {
320+
resolve(data)
321+
}
322+
323+
resolve({})
324+
}
325+
)
326+
})
327+
304328
const getBucketAdditionalInfo = async (s3: S3, name: BucketName) =>
305329
new Promise<any>(async resolve => {
306330
const promises = [
@@ -318,6 +342,7 @@ const getBucketAdditionalInfo = async (s3: S3, name: BucketName) =>
318342
getBucketTagging(s3, name),
319343
getBucketVersioning(s3, name),
320344
getBucketWebsite(s3, name),
345+
getBucketNotificationConfiguration(s3, name),
321346
]
322347

323348
const [
@@ -335,6 +360,7 @@ const getBucketAdditionalInfo = async (s3: S3, name: BucketName) =>
335360
Tags,
336361
VersioningInfo,
337362
WebsiteInfo,
363+
NotificationConfig,
338364
] = (await Promise.allSettled(promises)).map(
339365
/** We force the PromiseFulfilledResult interface
340366
* because all promises that we input to Promise.allSettled
@@ -364,6 +390,7 @@ const getBucketAdditionalInfo = async (s3: S3, name: BucketName) =>
364390
Tags: convertAwsTagsToTagMap(Tags),
365391
VersioningInfo,
366392
StaticWebsiteInfo: WebsiteInfo,
393+
NotificationConfiguration: NotificationConfig,
367394
})
368395
})
369396

@@ -455,6 +482,7 @@ export interface RawAwsS3 {
455482
ReqPaymentConfig: Payer
456483
StaticWebsiteInfo?: GetBucketWebsiteOutput
457484
VersioningInfo?: GetBucketVersioningOutput
485+
NotificationConfiguration?: NotificationConfiguration
458486
}
459487
Tags: TagMap
460488
Contents?: S3Object[]

src/services/s3/format.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import isEmpty from 'lodash/isEmpty'
33

44
import {
55
GetBucketVersioningOutput,
6+
NotificationConfiguration,
67
Policy,
78
PolicyStatus,
89
PublicAccessBlockConfiguration,
@@ -47,6 +48,7 @@ export default ({
4748
ReqPaymentConfig: reqPaymentConfig,
4849
StaticWebsiteInfo: staticWebsiteInfo,
4950
VersioningInfo: versioningInfo,
51+
NotificationConfiguration: notificationConfiguration,
5052
} = {
5153
AccelerationConfig: '',
5254
BucketOwnerData: { DisplayName: '' },
@@ -62,6 +64,7 @@ export default ({
6264
ReqPaymentConfig: '',
6365
StaticWebsiteInfo: {},
6466
VersioningInfo: {},
67+
NotificationConfiguration: {},
6568
},
6669
} = rawData
6770

@@ -172,6 +175,52 @@ export default ({
172175
})
173176
}
174177

178+
let notificationConfigurationData = {
179+
topicConfigurations: [],
180+
queueConfigurations: [],
181+
lambdaFunctionConfigurations: [],
182+
}
183+
184+
if (!isEmpty(notificationConfiguration)) {
185+
const {
186+
TopicConfigurations: topicConfigurations = [],
187+
QueueConfigurations: queueConfigurations = [],
188+
LambdaFunctionConfigurations: lambdaFunctionConfigurations = [],
189+
}: NotificationConfiguration = notificationConfiguration
190+
notificationConfigurationData = {
191+
topicConfigurations: topicConfigurations?.map(tc => ({
192+
id: tc.Id || cuid(),
193+
topicArn: tc.TopicArn,
194+
events: tc.Events || [],
195+
filterRules: tc.Filter?.Key?.FilterRules?.map(r => ({
196+
id: cuid(),
197+
name: r.Name,
198+
value: r.Value,
199+
})) || [],
200+
})) || [],
201+
queueConfigurations: queueConfigurations?.map(qc => ({
202+
id: qc.Id || cuid(),
203+
queueArn: qc.QueueArn,
204+
events: qc.Events || [],
205+
filterRules: qc.Filter?.Key?.FilterRules?.map(r => ({
206+
id: cuid(),
207+
name: r.Name,
208+
value: r.Value,
209+
})) || [],
210+
})) || [],
211+
lambdaFunctionConfigurations: lambdaFunctionConfigurations?.map(lc => ({
212+
id: lc.Id || cuid(),
213+
lambdaFunctionArn: lc.LambdaFunctionArn,
214+
events: lc.Events || [],
215+
filterRules: lc.Filter?.Key?.FilterRules?.map(r => ({
216+
id: cuid(),
217+
name: r.Name,
218+
value: r.Value,
219+
})) || [],
220+
})) || [],
221+
}
222+
}
223+
175224
// // Format S3 Tags
176225
const s3Tags = formatTagsFromMap(tags)
177226

@@ -201,6 +250,7 @@ export default ({
201250
? `${awsBucketItemsLimit}+`
202251
: `${total}`,
203252
transferAcceleration: accelerationStatus,
253+
notificationConfiguration: notificationConfigurationData,
204254
}
205255
return s3
206256
}

src/services/s3/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { Service } from '@cloudgraph/sdk'
22
import BaseService from '../base'
33
import format from './format'
44
import getData from './data'
5+
import getConnections from './connections'
56
import mutation from './mutation'
67

78
export default class S3 extends BaseService implements Service {
89
format = format.bind(this)
910

1011
getData = getData.bind(this)
1112

13+
getConnections = getConnections.bind(this)
14+
1215
mutation = mutation
1316
}

0 commit comments

Comments
 (0)