Skip to content

Commit f44abfc

Browse files
authored
Merge pull request #9 from cloudgraphdev/feature/CG-1061
Feature/cg 1061
2 parents 7c347c3 + b735e77 commit f44abfc

File tree

16 files changed

+261
-68
lines changed

16 files changed

+261
-68
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
9090
| dynamodb | appSync |
9191
| dmsReplicationInstance | securityGroup, subnet, vpc, kms |
9292
| ebs | asg, ec2, emrInstance |
93-
| ec2 | alb, asg, ebs, eip, emrInstance, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer |
93+
| ec2 | alb, asg, ebs, eip, emrInstance, eksCluster, elasticBeanstalkEnv, iamInstanceProfile, iamRole, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer |
9494
| ecr | |
9595
| ecsCluster | ecsService, ecsTask, ecsTaskSet |
9696
| ecsContainer | ecsTask, ec2 |
@@ -101,11 +101,11 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
101101
| efs | kms |
102102
| efsMountTarget | networkInterface, subnet, vpc |
103103
| eip | ec2, networkInterface, vpc |
104-
| eksCluster | iamRole, kms, securityGroup, subnet, vpc |
104+
| eksCluster | ec2, iamRole, kms, securityGroup, subnet, vpc |
105105
| elastiCacheCluster | securityGroup, subnet, vpc |
106106
| elastiCacheReplicationGroup | kms |
107107
| elasticBeanstalkApp | elasticBeanstalkEnv |
108-
| elasticBeanstalkEnv | elasticBeanstalkApp |
108+
| elasticBeanstalkEnv | ec2, elasticBeanstalkApp |
109109
| elasticSearchDomain | kms, securityGroup, subnet, vpc |
110110
| elb | cloudfront, ecsService, securityGroup, subnet, vpc |
111111
| emrCluster | kms, subnet |
@@ -115,14 +115,14 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi
115115
| glueJob | iamRole |
116116
| glueRegistry | |
117117
| guardDutyDetector | iamRole |
118-
| iamInstanceProfile | iamRole |
118+
| iamInstanceProfile | ec2, iamRole |
119119
| iamPasswordPolicy | |
120120
| iamSamlProvider | |
121121
| iamOpenIdConnectProvider | |
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, ec2, iamInstanceProfile, iamPolicy, eksCluster, ecsService, flowLog, glueJob, managedAirflow, sageMakerNotebookInstance, systemsManagerInstance guardDutyDetector |
126126
| iamGroup | iamUser, iamPolicy |
127127
| igw | vpc |
128128
| iot | |

src/enums/relations.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export default {
88
route53HostedZone: ['route53Record'],
99
emrCluster: ['emrInstance', 'emrStep'],
1010
ecsService: ['ecsTaskSet', 'ecsTaskDefinition'],
11+
iamInstanceProfile: ['ec2Instance'],
1112
}

src/services/ec2/connections.ts

Lines changed: 120 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ import { RawAwsSubnet } from '../subnet/data'
1616
import { RawAwsEcsContainer } from '../ecsContainer/data'
1717
import { RawAwsSystemsManagerInstance } from '../systemsManagerInstance/data'
1818
import { ssmManagedInstanceArn } from '../../utils/generateArns'
19+
import { RawAwsElasticBeanstalkEnv } from '../elasticBeanstalkEnvironment/data'
20+
import { RawAwsEksCluster } from '../eksCluster/data'
21+
import { getEksClusterName, getElasticBeanstalkEnvId } from './utils'
22+
import { RawAwsInstanceProfile } from '../iamInstanceProfile/data'
23+
import { globalRegionName } from '../../enums/regions'
24+
import { RawAwsIamRole } from '../iamRole/data'
1925

2026
/**
2127
* EC2
@@ -35,6 +41,7 @@ export default ({
3541
KeyPairName?: string
3642
Tags?: TagList
3743
IamInstanceProfile: IamInstanceProfile
44+
IamRolesArn?: string[]
3845
}
3946
region: string
4047
}): { [key: string]: ServiceConnection[] } => {
@@ -44,6 +51,9 @@ export default ({
4451
SecurityGroups: instanceSecurityGroups = [],
4552
NetworkInterfaces: instanceNetworkInterfaces = [],
4653
SubnetId: subnetId,
54+
Tags: tags,
55+
IamInstanceProfile: iamInstanceProfile,
56+
IamRolesArn: rolesArn,
4757
} = instance
4858

4959
/**
@@ -189,17 +199,37 @@ export default ({
189199
id: subnet.SubnetId,
190200
resourceType: services.subnet,
191201
relation: 'child',
192-
field: 'subnet',
202+
field: 'subnets',
193203
})
194204
}
195205
}
196206
}
197207

198208
/**
199-
* Find EKS
209+
* Find EKS cluster
200210
* related to this EC2
201211
*/
202-
// TODO: Implement when eks service is ready
212+
const eksClusterName = getEksClusterName(tags)
213+
const eksClusters: {
214+
name: string
215+
data: { [property: string]: any[] }
216+
} = data.find(({ name }) => name === services.eksCluster)
217+
if (eksClusters?.data?.[region]) {
218+
const eksClustersInRegion: RawAwsEksCluster[] = eksClusters.data[
219+
region
220+
].filter(({ name }: RawAwsEksCluster) => name === eksClusterName)
221+
222+
if (!isEmpty(eksClustersInRegion)) {
223+
for (const eksCluster of eksClustersInRegion) {
224+
connections.push({
225+
id: eksCluster.arn,
226+
resourceType: services.eksCluster,
227+
relation: 'child',
228+
field: 'eksCluster',
229+
})
230+
}
231+
}
232+
}
203233

204234
/**
205235
* Find ECS Container
@@ -210,9 +240,9 @@ export default ({
210240
data: { [property: string]: any[] }
211241
} = data.find(({ name }) => name === services.ecsContainer)
212242
if (ecsContainers?.data?.[region]) {
213-
const containersInRegion: RawAwsEcsContainer[] = ecsContainers.data[region].filter(
214-
({ ec2InstanceId }) => ec2InstanceId === id
215-
)
243+
const containersInRegion: RawAwsEcsContainer[] = ecsContainers.data[
244+
region
245+
].filter(({ ec2InstanceId }) => ec2InstanceId === id)
216246

217247
if (!isEmpty(containersInRegion)) {
218248
for (const container of containersInRegion) {
@@ -230,18 +260,24 @@ export default ({
230260
* Find SSM managed instances
231261
* related to this EC2 instance
232262
*/
233-
const instances: {
263+
const instances: {
234264
name: string
235265
data: { [property: string]: any[] }
236266
} = data.find(({ name }) => name === services.systemsManagerInstance)
237267
if (instances?.data?.[region]) {
238-
const dataInRegion: RawAwsSystemsManagerInstance[] = instances.data[region].filter(
268+
const dataInRegion: RawAwsSystemsManagerInstance[] = instances.data[
269+
region
270+
].filter(
239271
({ InstanceId }: RawAwsSystemsManagerInstance) => InstanceId === id
240272
)
241273

242274
if (!isEmpty(dataInRegion)) {
243275
for (const ssmInstance of dataInRegion) {
244-
const arn = ssmManagedInstanceArn({ region, account, name: ssmInstance.InstanceId })
276+
const arn = ssmManagedInstanceArn({
277+
region,
278+
account,
279+
name: ssmInstance.InstanceId,
280+
})
245281
connections.push({
246282
id: arn,
247283
resourceType: services.systemsManagerInstance,
@@ -254,9 +290,82 @@ export default ({
254290

255291
/**
256292
* Find Elastic Beanstalk
257-
* related to this EC2 loadbalancer
293+
* related to this EC2 instance
294+
*/
295+
const elasticBeanstalkEnvId = getElasticBeanstalkEnvId(tags)
296+
const elasticBeanstalkEnvs: {
297+
name: string
298+
data: { [property: string]: any[] }
299+
} = data.find(({ name }) => name === services.elasticBeanstalkEnv)
300+
if (elasticBeanstalkEnvs?.data?.[region]) {
301+
const elasticBeanstalkEnvsInRegion: RawAwsElasticBeanstalkEnv[] =
302+
elasticBeanstalkEnvs.data[region].filter(
303+
({ EnvironmentId }: RawAwsElasticBeanstalkEnv) =>
304+
elasticBeanstalkEnvId === EnvironmentId
305+
)
306+
307+
if (!isEmpty(elasticBeanstalkEnvsInRegion)) {
308+
for (const elasticBeanstalkEnv of elasticBeanstalkEnvsInRegion) {
309+
connections.push({
310+
id: elasticBeanstalkEnv.EnvironmentId,
311+
resourceType: services.elasticBeanstalkEnv,
312+
relation: 'child',
313+
field: 'elasticBeanstalkEnv',
314+
})
315+
}
316+
}
317+
}
318+
319+
/**
320+
* Find IAM Instance Profiles
321+
* related to this EC2 instance
322+
*/
323+
const iamInstanceProfiles: {
324+
name: string
325+
data: { [property: string]: any[] }
326+
} = data.find(({ name }) => name === services.iamInstanceProfile)
327+
if (iamInstanceProfiles?.data?.[globalRegionName]) {
328+
const dataAtRegion: RawAwsInstanceProfile[] = iamInstanceProfiles.data[
329+
globalRegionName
330+
].filter(instanceProfile => instanceProfile.Arn === iamInstanceProfile?.Arn)
331+
if (!isEmpty(dataAtRegion)) {
332+
for (const iamInstance of dataAtRegion) {
333+
const { InstanceProfileId: instanceProfileId }: RawAwsInstanceProfile = iamInstance
334+
335+
connections.push({
336+
id: instanceProfileId,
337+
resourceType: services.iamInstanceProfile,
338+
relation: 'child',
339+
field: 'iamInstanceProfile',
340+
})
341+
}
342+
}
343+
}
344+
345+
/**
346+
* Find IAM Roles
347+
* related to this EC2 instance
258348
*/
259-
// TODO: Implement when eb service is ready
349+
const roles: { name: string; data: { [property: string]: any[] } } =
350+
data.find(({ name }) => name === services.iamRole)
351+
352+
if (roles?.data?.[globalRegionName]) {
353+
const dataAtRegion: RawAwsIamRole[] = roles.data[globalRegionName].filter(
354+
({ Arn }: RawAwsIamRole) => rolesArn?.includes(Arn)
355+
)
356+
if (!isEmpty(dataAtRegion)) {
357+
for (const iamRole of dataAtRegion) {
358+
const { Arn: arn } :RawAwsIamRole = iamRole
359+
360+
connections.push({
361+
id: arn,
362+
resourceType: services.iamRole,
363+
relation: 'child',
364+
field: 'iamRole',
365+
})
366+
}
367+
}
368+
}
260369

261370
const ec2Result = {
262371
[id]: connections,

src/services/ec2/data.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import awsLoggerText from '../../properties/logger'
3131
import { initTestEndpoint } from '../../utils'
3232
import AwsErrorLog from '../../utils/errorLog'
3333
import { convertAwsTagsToTagMap } from '../../utils/format'
34+
import getIamInstanceProfiles, {
35+
RawAwsInstanceProfile,
36+
} from '../iamInstanceProfile/data'
3437

3538
const lt = { ...awsLoggerText }
3639
const { logger } = CloudGraph
@@ -54,6 +57,7 @@ export interface RawAwsEC2 extends Omit<Instance, 'Tags'> {
5457
IamInstanceProfile?: IamInstanceProfile
5558
cloudWatchMetricData?: any
5659
PlatformDetails?: string
60+
IamRolesArn?: string[]
5761
}
5862

5963
/**
@@ -63,9 +67,11 @@ export interface RawAwsEC2 extends Omit<Instance, 'Tags'> {
6367
export default async ({
6468
regions,
6569
config,
70+
rawData,
6671
}: {
6772
regions: string
6873
config: Config
74+
rawData: any
6975
}): Promise<{
7076
[region: string]: RawAwsEC2[]
7177
}> =>
@@ -625,6 +631,25 @@ export default async ({
625631
iamInstanceProfile[InstanceId] || {}
626632
})
627633

634+
// populate ec2Instances with the iamRoles Arn
635+
const iamInstancesProfiles: RawAwsInstanceProfile[] =
636+
Object.values(
637+
await getIamInstanceProfiles({
638+
config,
639+
rawData,
640+
})
641+
)?.reduce((acc, val) => acc.concat(val), []) || []
642+
643+
ec2Instances.map(({ IamInstanceProfile: instanceProfile }, ec2Idx) => {
644+
const instance = iamInstancesProfiles.find(
645+
i => i.Arn === instanceProfile?.Arn
646+
)
647+
if (instance) {
648+
ec2Instances[ec2Idx].IamRolesArn =
649+
instance?.Roles?.map(r => r.Arn) || []
650+
}
651+
})
652+
628653
errorLog.reset()
629654

630655
/**

src/services/ec2/format.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export default ({
4545
fingerprint,
4646
tags: keyPairTags = {},
4747
} = {},
48-
IamInstanceProfile: iamInstanceProfile,
4948
cloudWatchMetricData,
5049
PlatformDetails: platformDetails,
5150
InstanceLifecycle: instanceLifecycle,
@@ -114,7 +113,6 @@ export default ({
114113
sourceDestCheck: sourceDestCheck ? t.yes : t.no,
115114
availabilityZone: placement?.AvailabilityZone || '',
116115
cpuThreadsPerCore: cpuOptions?.ThreadsPerCore || 0,
117-
iamInstanceProfile: iamInstanceProfile?.Arn || '',
118116
deletionProtection: deletionProtection ? t.yes : t.no,
119117
primaryNetworkInterface: networkInterfaceId,
120118
metadataOptions: {

src/services/ec2/schema.graphql

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ type awsEc2 implements awsBaseService @key(fields: "arn") {
9292
sourceDestCheck: String @search(by: [hash, regexp])
9393
availabilityZone: String @search(by: [hash, regexp])
9494
cpuThreadsPerCore: Int @search
95-
iamInstanceProfile: String @search(by: [hash, regexp]) # TODO: use to make a connection to a iamRole
9695
deletionProtection: String @search(by: [hash, regexp])
9796
dailyCost: awsTotalBillingInfo
9897
primaryNetworkInterface: String @search(by: [hash, regexp])
@@ -112,9 +111,12 @@ type awsEc2 implements awsBaseService @key(fields: "arn") {
112111
eip: [awsEip] @hasInverse(field: ec2Instance)
113112
networkInterfaces: [awsNetworkInterface] @hasInverse(field: ec2Instance)
114113
securityGroups: [awsSecurityGroup] @hasInverse(field: ec2Instance)
115-
subnet: [awsSubnet] @hasInverse(field: ec2Instance) #change to plural
116-
ecsContainer: [awsEcsContainer] @hasInverse(field: ec2Instance) #change to plural
114+
subnets: [awsSubnet] @hasInverse(field: ec2Instances)
115+
ecsContainer: [awsEcsContainer] @hasInverse(field: ec2Instances)
117116
emrInstance: [awsEmrInstance] @hasInverse(field: ec2Instance)
118-
systemsManagerInstance: [awsSystemsManagerInstance]
119-
@hasInverse(field: ec2Instance)
117+
systemsManagerInstance: [awsSystemsManagerInstance] @hasInverse(field: ec2Instance)
118+
eksCluster: [awsEksCluster] @hasInverse(field: ec2Instances)
119+
elasticBeanstalkEnv: [awsElasticBeanstalkEnv] @hasInverse(field: ec2Instances)
120+
iamInstanceProfile: [awsIamInstanceProfile] @hasInverse(field: ec2Instances)
121+
iamRole: [awsIamRole] @hasInverse(field: ec2Instances)
120122
}

src/services/ec2/utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Tag } from 'aws-sdk/clients/ec2'
2+
import { isEmpty, last } from 'lodash'
3+
4+
const clusterTag = 'kubernetes.io/cluster/'
5+
const environmentIdTag = 'elasticbeanstalk:environment-id'
6+
7+
export const getEksClusterName = (tags?: Tag[]): string => {
8+
if (isEmpty(tags)) {
9+
return ''
10+
}
11+
let eksClusterName = ''
12+
Object.keys(tags)?.some(key => {
13+
const isMatch = key.includes(clusterTag)
14+
if (isMatch) {
15+
eksClusterName = last(key.split('/'))
16+
}
17+
return isMatch
18+
})
19+
return eksClusterName
20+
}
21+
22+
export const getElasticBeanstalkEnvId = (tags?: Tag[]): string => {
23+
if (isEmpty(tags)) {
24+
return ''
25+
}
26+
let elasticBeanstalkId = ''
27+
Object.keys(tags)?.some(key => {
28+
const isMatch = key.includes(environmentIdTag)
29+
if (isMatch) {
30+
elasticBeanstalkId = tags[key]
31+
}
32+
return isMatch
33+
})
34+
return elasticBeanstalkId
35+
}

0 commit comments

Comments
 (0)