diff --git a/README.md b/README.md index 56f53157..7ad8777d 100644 --- a/README.md +++ b/README.md @@ -69,12 +69,12 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | Service | Relations | | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| alb | ec2, route53Record, securityGroup, subnet, vpc | +| alb | ec2, elasticBeanstalkEnv, route53Record, securityGroup, subnet, vpc | | apiGatewayRestApi | apiGatewayResource, apiGatewayStage, route53Record | | apiGatewayStage | apiGatewayRestApi | | apiGatewayResource | apiGatewayRestApi | | appSync | cognitoUserPool, dynamodb, iamRole, lambda, rdsCluster, wafV2WebAcl | -| asg | ebs, ec2, iamRole, securityGroup, subnet | +| asg | ebs, ec2, elasticBeanstalkEnv, iamRole, securityGroup, subnet | | athenaDataCatalog | | | clientVpnEndpoint | securityGroup | | cloud9 | | @@ -107,9 +107,9 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | elastiCacheCluster | securityGroup, subnet, vpc | | elastiCacheReplicationGroup | kms | | elasticBeanstalkApp | elasticBeanstalkEnv, iamRole | -| elasticBeanstalkEnv | ec2, elasticBeanstalkApp, iamRole | +| elasticBeanstalkEnv | alb, asg, ec2, elb, elasticBeanstalkApp, iamRole, sqs | | elasticSearchDomain | kms, securityGroup, subnet, vpc | -| elb | cloudfront, ecsService, securityGroup, subnet, vpc | +| elb | cloudfront, ecsService, elasticBeanstalkEnv, securityGroup, subnet, vpc | | emrCluster | kms, subnet | | emrInstance | ebs, ec2 | | emrStep | | @@ -152,7 +152,7 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance, networkInterface | | ses | | | sns | kms, cloudtrail, cloudwatch, s3 | -| sqs | s3 | +| sqs | elasticBeanstalkEnv, s3 | | subnet | alb, asg, codebuild, dmsReplicationInstance, ec2, ecsService, efsMountTarget, elastiCacheCluster, elasticSearchDomain, elb, lambda, managedAirflow, natGateway, networkInterface, rdsCluster, sageMakerNotebookInstance, routeTable, vpc, eksCluster, emrCluster, flowLog | | systemsManagerInstance | ec2, iamRole | | systemsManagerDocument | | diff --git a/src/services/alb/schema.graphql b/src/services/alb/schema.graphql index c6e167b8..9a4b0e5a 100644 --- a/src/services/alb/schema.graphql +++ b/src/services/alb/schema.graphql @@ -21,6 +21,7 @@ type awsAlb implements awsBaseService @key(fields: "arn") { route53Record: [awsRoute53Record] @hasInverse(field: alb) #change to plural listeners: [awsAlbListener] subnet: [awsSubnet] @hasInverse(field: alb) #change to plural + elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: albs) } type awsAlbListener diff --git a/src/services/asg/schema.graphql b/src/services/asg/schema.graphql index 9aa98db8..f4a42c10 100644 --- a/src/services/asg/schema.graphql +++ b/src/services/asg/schema.graphql @@ -131,6 +131,7 @@ type awsAsg implements awsBaseService @key(fields: "arn") { tags: [awsRawTag] launchConfiguration: awsLaunchConfiguration ec2Instance: [awsEc2] @hasInverse(field: asg) #change to plural + elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: asgs) securityGroups: [awsSecurityGroup] @hasInverse(field: asg) ebs: [awsEbs] @hasInverse(field: asg) subnet: [awsSubnet] @hasInverse(field: asg) #change to plural diff --git a/src/services/ec2/connections.ts b/src/services/ec2/connections.ts index 36ff64c5..49339793 100644 --- a/src/services/ec2/connections.ts +++ b/src/services/ec2/connections.ts @@ -310,7 +310,7 @@ export default ({ id: elasticBeanstalkEnv.EnvironmentId, resourceType: services.elasticBeanstalkEnv, relation: 'child', - field: 'elasticBeanstalkEnv', + field: 'elasticBeanstalkEnvs', }) } } diff --git a/src/services/ec2/schema.graphql b/src/services/ec2/schema.graphql index 21b7c201..7ae9be65 100644 --- a/src/services/ec2/schema.graphql +++ b/src/services/ec2/schema.graphql @@ -108,15 +108,15 @@ type awsEc2 implements awsBaseService @key(fields: "arn") { alb: [awsAlb] @hasInverse(field: ec2Instance) asg: [awsAsg] @hasInverse(field: ec2Instance) ebs: [awsEbs] @hasInverse(field: ec2Instance) - eip: [awsEip] @hasInverse(field: ec2Instance) - networkInterfaces: [awsNetworkInterface] @hasInverse(field: ec2Instance) - securityGroups: [awsSecurityGroup] @hasInverse(field: ec2Instance) - subnets: [awsSubnet] @hasInverse(field: ec2Instances) ecsContainer: [awsEcsContainer] @hasInverse(field: ec2Instances) - emrInstance: [awsEmrInstance] @hasInverse(field: ec2Instance) - systemsManagerInstance: [awsSystemsManagerInstance] @hasInverse(field: ec2Instance) + eip: [awsEip] @hasInverse(field: ec2Instance) eksCluster: [awsEksCluster] @hasInverse(field: ec2Instances) elasticBeanstalkEnv: [awsElasticBeanstalkEnv] @hasInverse(field: ec2Instances) + emrInstance: [awsEmrInstance] @hasInverse(field: ec2Instance) iamInstanceProfile: [awsIamInstanceProfile] @hasInverse(field: ec2Instances) iamRole: [awsIamRole] @hasInverse(field: ec2Instances) + networkInterfaces: [awsNetworkInterface] @hasInverse(field: ec2Instance) + securityGroups: [awsSecurityGroup] @hasInverse(field: ec2Instance) + subnets: [awsSubnet] @hasInverse(field: ec2Instances) + systemsManagerInstance: [awsSystemsManagerInstance] @hasInverse(field: ec2Instance) } diff --git a/src/services/elasticBeanstalkApplication/schema.graphql b/src/services/elasticBeanstalkApplication/schema.graphql index 3ae84666..b7e150e5 100644 --- a/src/services/elasticBeanstalkApplication/schema.graphql +++ b/src/services/elasticBeanstalkApplication/schema.graphql @@ -2,7 +2,7 @@ type awsElasticBeanstalkApp implements awsBaseService @key(fields: "arn") { name: String @search(by: [hash, regexp]) description: String @search(by: [hash, regexp]) iamServiceRole: String @search(by: [hash, regexp]) - elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: elasticBeanstalkApps) + elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: elasticBeanstalkApp) tags: [awsRawTag] iamRole: [awsIamRole] @hasInverse(field: elasticBeanstalkApps) } diff --git a/src/services/elasticBeanstalkEnvironment/connections.ts b/src/services/elasticBeanstalkEnvironment/connections.ts index 68c91d20..d44b0098 100644 --- a/src/services/elasticBeanstalkEnvironment/connections.ts +++ b/src/services/elasticBeanstalkEnvironment/connections.ts @@ -5,6 +5,13 @@ import { ServiceConnection } from '@cloudgraph/sdk' import services from '../../enums/services' import { RawAwsElasticBeanstalkEnv } from './data' import { RawAwsElasticBeanstalkApp } from '../elasticBeanstalkApplication/data' +import { RawAwsAsg } from '../asg/data' +import { RawAwsEC2 } from '../ec2/data' +import { caseInsensitiveIncludes } from '../../utils/index' +import { RawAwsElb } from '../elb/data' +import { RawAwsAlb } from '../alb/data' +import { AwsSqs } from '../sqs/data' +import { elbArn } from '../../utils/generateArns' /** * Elastic Beanstalk Environment @@ -14,6 +21,7 @@ export default ({ service: environment, data, region, + account, }: { account: string data: { name: string; data: { [property: string]: any[] } }[] @@ -21,7 +29,21 @@ export default ({ region: string }): { [key: string]: ServiceConnection[] } => { const connections: ServiceConnection[] = [] - const { EnvironmentId: id, ApplicationName: appName } = environment + const { + EnvironmentId: id, + ApplicationName: appName, + resources: { + AutoScalingGroups = [], + Instances = [], + LoadBalancers = [], + Queues = [], + }, + } = environment + + const asgNames = AutoScalingGroups.map(({ Name }) => Name) + const ec2Ids = Instances.map(({ Id }) => Id) + const lbNames = LoadBalancers.map(({ Name }) => Name) + const queuesUrls = Queues.map(({ URL }) => URL) /** * Find Elastic Beanstalk environments @@ -44,7 +66,140 @@ export default ({ id: app.ApplicationArn, resourceType: services.elasticBeanstalkApp, relation: 'child', - field: 'elasticBeanstalkApps', + field: 'elasticBeanstalkApp', + }) + } + } + } + + /** + * Find Auto Scaling Groups + * related to this Elastic Beanstalk application + */ + const asgs: { + name: string + data: { [property: string]: RawAwsAsg[] } + } = data.find(({ name }) => name === services.asg) + + if (asgs?.data?.[region]) { + const asgsInRegion: RawAwsAsg[] = asgs.data[region].filter( + ({ AutoScalingGroupName }) => + caseInsensitiveIncludes(asgNames, AutoScalingGroupName) + ) + + if (!isEmpty(asgsInRegion)) { + for (const asg of asgsInRegion) { + connections.push({ + id: asg.AutoScalingGroupARN, + resourceType: services.asg, + relation: 'child', + field: 'asgs', + }) + } + } + } + + /** + * Find EC2 Instances + * related to this Elastic Beanstalk application + */ + const ec2Instances: { + name: string + data: { [property: string]: RawAwsEC2[] } + } = data.find(({ name }) => name === services.ec2Instance) + + if (ec2Instances?.data?.[region]) { + const ec2InstancesInRegion: RawAwsEC2[] = ec2Instances.data[region].filter( + ({ InstanceId }) => caseInsensitiveIncludes(ec2Ids, InstanceId) + ) + + if (!isEmpty(ec2InstancesInRegion)) { + for (const ec2Instance of ec2InstancesInRegion) { + connections.push({ + id: ec2Instance.InstanceId, + resourceType: services.ec2Instance, + relation: 'child', + field: 'ec2Instances', + }) + } + } + } + + /** + * Find Load Balancers(ELB) + * related to this Elastic Beanstalk application + */ + const elbs: { + name: string + data: { [property: string]: RawAwsElb[] } + } = data.find(({ name }) => name === services.elb) + + if (elbs?.data?.[region]) { + const elbsInRegion: RawAwsElb[] = elbs.data[region].filter( + ({ LoadBalancerName }) => + caseInsensitiveIncludes(lbNames, LoadBalancerName) + ) + + if (!isEmpty(elbsInRegion)) { + for (const elb of elbsInRegion) { + connections.push({ + id: elbArn({ region, account, name: elb.LoadBalancerName }), + resourceType: services.elb, + relation: 'child', + field: 'elbs', + }) + } + } + } + + /** + * Find Load Balancers(ALB) + * related to this Elastic Beanstalk application + */ + const albs: { + name: string + data: { [property: string]: RawAwsAlb[] } + } = data.find(({ name }) => name === services.alb) + + if (albs?.data?.[region]) { + const albsInRegion: RawAwsAlb[] = albs.data[region].filter( + ({ LoadBalancerName }) => + caseInsensitiveIncludes(lbNames, LoadBalancerName) + ) + + if (!isEmpty(albsInRegion)) { + for (const alb of albsInRegion) { + connections.push({ + id: alb.LoadBalancerArn, + resourceType: services.alb, + relation: 'child', + field: 'albs', + }) + } + } + } + + /** + * Find Load Balancers(SQS) + * related to this Elastic Beanstalk application + */ + const sqs: { + name: string + data: { [property: string]: AwsSqs[] } + } = data.find(({ name }) => name === services.sqs) + + if (sqs?.data?.[region]) { + const sqsInRegion: AwsSqs[] = sqs.data[region].filter(({ queueUrl }) => + caseInsensitiveIncludes(queuesUrls, queueUrl) + ) + + if (!isEmpty(sqsInRegion)) { + for (const s of sqsInRegion) { + connections.push({ + id: s.sqsAttributes.QueueArn, + resourceType: services.sqs, + relation: 'child', + field: 'sqsQueues', }) } } diff --git a/src/services/elasticBeanstalkEnvironment/schema.graphql b/src/services/elasticBeanstalkEnvironment/schema.graphql index f085334f..c10acb8a 100644 --- a/src/services/elasticBeanstalkEnvironment/schema.graphql +++ b/src/services/elasticBeanstalkEnvironment/schema.graphql @@ -10,10 +10,14 @@ type awsElasticBeanstalkEnv implements awsBaseService @key(fields: "arn") { solutionStackName: String @search(by: [hash, regexp]) tier: String @search(by: [hash]) versionLabel: String @search(by: [hash, regexp]) + asgs: [awsAsg] @hasInverse(field: elasticBeanstalkEnvs) + albs: [awsAlb] @hasInverse(field: elasticBeanstalkEnvs) + ec2Instances: [awsEc2] @hasInverse(field: elasticBeanstalkEnv) + elbs: [awsElb] @hasInverse(field: elasticBeanstalkEnvs) + elasticBeanstalkApp: [awsElasticBeanstalkApp] @hasInverse(field: elasticBeanstalkEnvs) iamRole: [awsIamRole] @hasInverse(field: elasticBeanstalkEnvs) + sqsQueues: [awsSqs] @hasInverse(field: elasticBeanstalkEnvs) tags: [awsRawTag] - elasticBeanstalkApps: [awsElasticBeanstalkApp] @hasInverse(field: elasticBeanstalkEnvs) - ec2Instances: [awsEc2] @hasInverse(field: elasticBeanstalkEnv) } type awsElasticBeanstalkEnvSetting { diff --git a/src/services/elb/schema.graphql b/src/services/elb/schema.graphql index 819c06b9..28e15dd7 100644 --- a/src/services/elb/schema.graphql +++ b/src/services/elb/schema.graphql @@ -23,6 +23,7 @@ type awsElb implements awsBaseService @key(fields: "arn") { route53Record: [awsRoute53Record] @hasInverse(field: elb) #change to plural subnet: [awsSubnet] @hasInverse(field: elb) #change to plural ecsService: [awsEcsService] @hasInverse(field: elb) + elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: elbs) } type awsElbSourceSecurityGroup diff --git a/src/services/sqs/schema.graphql b/src/services/sqs/schema.graphql index 12f96002..d71f19b8 100644 --- a/src/services/sqs/schema.graphql +++ b/src/services/sqs/schema.graphql @@ -17,6 +17,7 @@ type awsSqs implements awsBaseService @key(fields: "arn") { deduplicationScope: String @search(by: [hash, regexp]) fifoThroughputLimit: String @search(by: [hash, regexp]) contentBasedDeduplication: Boolean @search + elasticBeanstalkEnvs: [awsElasticBeanstalkEnv] @hasInverse(field: sqsQueues) tags: [awsRawTag] s3: [awsS3] @hasInverse(field: sqs) } diff --git a/src/types/generated.ts b/src/types/generated.ts index b737099b..7c911636 100644 --- a/src/types/generated.ts +++ b/src/types/generated.ts @@ -257,6 +257,7 @@ export type AwsAlb = AwsBaseService & { dnsName?: Maybe; dropInvalidHeaderFields?: Maybe; ec2Instance?: Maybe>>; + elasticBeanstalkEnvs?: Maybe>>; hostedZone?: Maybe; http2?: Maybe; idleTimeout?: Maybe; @@ -469,6 +470,7 @@ export type AwsAsg = AwsBaseService & { ebs?: Maybe>>; ec2Instance?: Maybe>>; ec2InstanceIds?: Maybe>>; + elasticBeanstalkEnvs?: Maybe>>; enabledMetrics?: Maybe>>; healthCheckGracePeriod?: Maybe; healthCheckType?: Maybe; @@ -2499,11 +2501,14 @@ export type AwsElasticBeanstalkApp = AwsBaseService & { }; export type AwsElasticBeanstalkEnv = AwsBaseService & { + albs?: Maybe>>; applicationName?: Maybe; + asgs?: Maybe>>; cname?: Maybe; description?: Maybe; ec2Instances?: Maybe>>; - elasticBeanstalkApps?: Maybe>>; + elasticBeanstalkApp?: Maybe>>; + elbs?: Maybe>>; endpointUrl?: Maybe; iamRole?: Maybe>>; name?: Maybe; @@ -2511,6 +2516,7 @@ export type AwsElasticBeanstalkEnv = AwsBaseService & { resources?: Maybe>>; settings?: Maybe>>; solutionStackName?: Maybe; + sqsQueues?: Maybe>>; tags?: Maybe>>; tier?: Maybe; versionLabel?: Maybe; @@ -2680,6 +2686,7 @@ export type AwsElb = AwsBaseService & { crossZoneLoadBalancing?: Maybe; dnsName?: Maybe; ecsService?: Maybe>>; + elasticBeanstalkEnvs?: Maybe>>; healthCheck?: Maybe; hostedZone?: Maybe; idleTimeout?: Maybe; @@ -3971,6 +3978,7 @@ export type AwsSqs = AwsBaseService & { contentBasedDeduplication?: Maybe; deduplicationScope?: Maybe; delaySeconds?: Maybe; + elasticBeanstalkEnvs?: Maybe>>; fifoQueue?: Maybe; fifoThroughputLimit?: Maybe; kmsDataKeyReusePeriodSeconds?: Maybe; diff --git a/src/utils/index.ts b/src/utils/index.ts index a96801b7..fed4ec9d 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -143,3 +143,7 @@ export const checkAndMergeConnections = ( } return connectionsToMerge } + +export const caseInsensitiveIncludes = (arr: string[], s1: string): boolean => + !isEmpty(arr) && + arr.filter(str => str.toLowerCase().includes(s1.toLowerCase())).length > 0