From 636e359bdc4e457733ee424fa7c873db3b03ee35 Mon Sep 17 00:00:00 2001 From: James Zhou Date: Thu, 4 May 2023 18:52:14 -0500 Subject: [PATCH 1/3] feat(CG-1342): add opsworks stack, app, instance service --- README.md | 15 +- src/enums/resources.ts | 3 + src/enums/schemasMap.ts | 3 + src/enums/serviceAliases.ts | 3 + src/enums/serviceMap.ts | 6 + src/enums/services.ts | 3 + src/properties/logger.ts | 9 + src/services/ec2/schema.graphql | 1 + src/services/opsworksApp/data.ts | 117 ++++++++++++ src/services/opsworksApp/format.ts | 89 ++++++++++ src/services/opsworksApp/index.ts | 13 ++ src/services/opsworksApp/mutation.ts | 5 + src/services/opsworksApp/schema.graphql | 76 ++++++++ src/services/opsworksInstance/connections.ts | 74 ++++++++ src/services/opsworksInstance/data.ts | 116 ++++++++++++ src/services/opsworksInstance/format.ts | 124 +++++++++++++ src/services/opsworksInstance/index.ts | 16 ++ src/services/opsworksInstance/mutation.ts | 5 + src/services/opsworksInstance/schema.graphql | 83 +++++++++ src/services/opsworksStack/connections.ts | 78 ++++++++ src/services/opsworksStack/data.ts | 103 +++++++++++ src/services/opsworksStack/format.ts | 84 +++++++++ src/services/opsworksStack/index.ts | 16 ++ src/services/opsworksStack/mutation.ts | 5 + src/services/opsworksStack/schema.graphql | 69 ++++++++ src/services/securityGroup/schema.graphql | 1 + src/services/subnet/schema.graphql | 2 + src/services/vpc/schema.graphql | 1 + src/types/generated.ts | 176 +++++++++++++++++++ src/utils/generateArns.ts | 13 +- 30 files changed, 1302 insertions(+), 7 deletions(-) create mode 100644 src/services/opsworksApp/data.ts create mode 100644 src/services/opsworksApp/format.ts create mode 100644 src/services/opsworksApp/index.ts create mode 100644 src/services/opsworksApp/mutation.ts create mode 100644 src/services/opsworksApp/schema.graphql create mode 100644 src/services/opsworksInstance/connections.ts create mode 100644 src/services/opsworksInstance/data.ts create mode 100644 src/services/opsworksInstance/format.ts create mode 100644 src/services/opsworksInstance/index.ts create mode 100644 src/services/opsworksInstance/mutation.ts create mode 100644 src/services/opsworksInstance/schema.graphql create mode 100644 src/services/opsworksStack/connections.ts create mode 100644 src/services/opsworksStack/data.ts create mode 100644 src/services/opsworksStack/format.ts create mode 100644 src/services/opsworksStack/index.ts create mode 100644 src/services/opsworksStack/mutation.ts create mode 100644 src/services/opsworksStack/schema.graphql diff --git a/README.md b/README.md index f445b43f..4093d13c 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,8 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | dynamodb | appSync, iamRole, kms | | dmsReplicationInstance | securityGroup, subnet, vpc, kms | | ebs | asg, ec2, emrInstance, ebsSnapshot | -| ebsSnapshot | ebs, kms | -| ec2 | alb, asg, ebs, eip, emrInstance, eksCluster, elasticBeanstalkEnv, iamInstanceProfile, iamRole, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer | +| ebsSnapshot | ebs, kms | +| ec2 | alb, asg, ebs, eip, emrInstance, eksCluster, elasticBeanstalkEnv, iamInstanceProfile, iamRole, networkInterface, securityGroup, subnet, systemsManagerInstance, vpc, ecsContainer, opsworksInstance | | ecr | | | ecsCluster | cloudwatchLog, ecsService, ecsTask, ecsTaskSet, kms, s3 | | ecsContainer | ecsTask, ec2 | @@ -121,7 +121,7 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | glueJob | iamRole | | glueRegistry | | | guardDutyDetector | iamRole | -| iamAccessAnalyzer | | +| iamAccessAnalyzer | | | iamInstanceProfile | ec2, iamRole | | iamPasswordPolicy | | | iamSamlProvider | cognitoIdentityPool | @@ -142,6 +142,9 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | nacl | vpc | | natGateway | networkInterface, subnet, vpc | | networkInterface | ec2, eip, efsMountTarget, natGateway, sageMakerNotebookInstance, subnet, vpc, vpcEndpoint, flowLog, securityGroup | +| opsworksApp | opsworksStack | +| opsworksInstance | opsworksStack, subnet, securityGroup, ec2Instance | +| opsworksStack | vpc, subnet, opsworksApp, opsworksInstance | | organization | | rdsCluster | appSync, rdsClusterSnapshot, rdsDbInstance, route53HostedZone, securityGroup, subnet, iamRole, kms | | rdsClusterSnapshot | kms, rdsCluster, vpc | @@ -155,18 +158,18 @@ CloudGraph AWS Provider will ask you what regions you would like to crawl and wi | sageMakerProject | | | s3 | cloudfront, cloudtrail, ecsCluster, iamRole, kinesisFirehose, kms, lambda, managedAirflow, sns, sqs | | secretsManager | kms, lambda | -| securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance, networkInterface, vpcEndpoint | +| securityGroup | alb, asg, clientVpnEndpoint, codebuild, dmsReplicationInstance, ecsService, lambda, ec2, elasticSearchDomain, elb, rdsCluster, rdsDbInstance, eksCluster, elastiCacheCluster, managedAirflow, sageMakerNotebookInstance, networkInterface, vpcEndpoint, opsworksInstance | | securityHub | | | ses | | | sns | kms, cloudtrail, cloudwatch, s3 | | sqs | elasticBeanstalkEnv, s3 | -| subnet | alb, asg, codebuild, dmsReplicationInstance, ec2, ecsService, efsMountTarget, elastiCacheCluster, elasticSearchDomain, elb, lambda, managedAirflow, natGateway, networkInterface, rdsCluster, sageMakerNotebookInstance, routeTable, vpc, vpcEndpoint, eksCluster, emrCluster, flowLog | +| subnet | alb, asg, codebuild, dmsReplicationInstance, ec2, ecsService, efsMountTarget, elastiCacheCluster, elasticSearchDomain, elb, lambda, managedAirflow, natGateway, networkInterface, rdsCluster, sageMakerNotebookInstance, routeTable, vpc, vpcEndpoint, eksCluster, emrCluster, flowLog, opsworksStack, opsworksInstance | | systemsManagerInstance | ec2, iamRole | | systemsManagerDocument | | | transitGateway | transitGatewayAttachment, transitGatewayRouteTable, vpnConnection | | transitGatewayAttachment | transitGateway, transitGatewayRouteTable, vpc, vpnConnection | | transitGatewayRouteTable | transitGateway, transitGatewayAttachment | -| vpc | alb, codebuild, dmsReplicationInstance, ec2, eip, elb, ecsService, efsMountTarget, eksCluster igw, elastiCacheCluster, elasticSearchDomain, lambda, nacl, natGateway, networkInterface, rdsClusterSnapshot, rdsDbInstance, redshiftCluster, route53HostedZone, routeTable, subnet, flowLog, vpnGateway, transitGatewayAttachment, vpcEndpoint, vpcPeeringConnection | +| vpc | alb, codebuild, dmsReplicationInstance, ec2, eip, elb, ecsService, efsMountTarget, eksCluster igw, elastiCacheCluster, elasticSearchDomain, lambda, nacl, natGateway, networkInterface, rdsClusterSnapshot, rdsDbInstance, redshiftCluster, route53HostedZone, routeTable, subnet, flowLog, vpnGateway, transitGatewayAttachment, vpcEndpoint, vpcPeeringConnection, opsworksStack | | vpcEndpoint | networkInterface, routeTable, securityGroup, subnet, vpc | | vpcPeeringConnection | vpc | | vpnConnection | customerGateway, transitGateway, transitGatewayAttachment, vpnGateway | diff --git a/src/enums/resources.ts b/src/enums/resources.ts index 13bca591..2ffc975b 100644 --- a/src/enums/resources.ts +++ b/src/enums/resources.ts @@ -47,6 +47,9 @@ export default { securityGroup: 'aws_security_group', securityHub: 'aws_security_hub', iamRolePolicy: 'aws_iam_role_policy', + opsWorksApp: 'aws_opsworks_application', + opsWorksStack: 'aws_opsworks_stack', + opsWorksInstance: 'aws_opsworks_instance', efsMountTarget: 'aws_efs_mount_target', route53ZRecord: 'aws_route53_record', lambdaFunction: 'aws_lambda_function', diff --git a/src/enums/schemasMap.ts b/src/enums/schemasMap.ts index 0667220c..579b21f1 100644 --- a/src/enums/schemasMap.ts +++ b/src/enums/schemasMap.ts @@ -78,6 +78,9 @@ export default { [services.nacl]: 'awsNetworkAcl', [services.nat]: 'awsNatGateway', [services.networkInterface]: 'awsNetworkInterface', + [services.opsWorksApp]: 'awsOpsWorksApp', + [services.opsWorksStack]: 'awsOpsWorksStack', + [services.opsWorksInstance]: 'awsOpsWorksInstance', [services.sg]: 'awsSecurityGroup', [services.securityHub]: 'awsSecurityHub', [services.subnet]: 'awsSubnet', diff --git a/src/enums/serviceAliases.ts b/src/enums/serviceAliases.ts index 8f7cf0c5..d8e92808 100644 --- a/src/enums/serviceAliases.ts +++ b/src/enums/serviceAliases.ts @@ -55,6 +55,9 @@ export default { [services.managedPrefixList]: 'managedPrefixLists', [services.nat]: 'natGateway', [services.networkInterface]: 'networkInterfaces', + [services.opsWorksApp]: 'opsWorksApps', + [services.opsWorksStack]: 'opsWorksStacks', + [services.opsWorksInstance]: 'opsWorksInstances', [services.organization]: 'organizations', [services.rdsCluster]: 'rdsClusters', [services.rdsClusterSnapshot]: 'rdsClusterSnapshots', diff --git a/src/enums/serviceMap.ts b/src/enums/serviceMap.ts index a063aec8..91168fce 100644 --- a/src/enums/serviceMap.ts +++ b/src/enums/serviceMap.ts @@ -47,6 +47,9 @@ import Lambda from '../services/lambda' import NATGateway from '../services/natGateway' import NetworkAcl from '../services/nacl' import NetworkInterface from '../services/networkInterface' +import OpsWorksApp from '../services/opsworksApp' +import OpsWorksStack from '../services/opsworksStack' +import OpsWorksInstance from '../services/opsworksInstance' import RDSCluster from '../services/rdsCluster' import RDSDbInstance from '../services/rdsDbInstance' import RedshiftCluster from '../services/redshift' @@ -167,6 +170,9 @@ export default { [services.nacl]: NetworkAcl, [services.nat]: NATGateway, [services.networkInterface]: NetworkInterface, + [services.opsWorksApp]: OpsWorksApp, + [services.opsWorksStack]: OpsWorksStack, + [services.opsWorksInstance]: OpsWorksInstance, [services.sg]: AwsSecurityGroup, [services.subnet]: AwsSubnet, [services.vpc]: VPC, diff --git a/src/enums/services.ts b/src/enums/services.ts index a8003147..7345d495 100644 --- a/src/enums/services.ts +++ b/src/enums/services.ts @@ -72,6 +72,9 @@ export default { nacl: 'nacl', nat: 'nat', networkInterface: 'networkInterface', + opsWorksApp: 'opsWorksApp', + opsWorksStack: 'opsWorksStack', + opsWorksInstance: 'opsWorksInstance', organization: 'organization', rdsCluster: 'rdsCluster', rdsClusterSnapshot: 'rdsClusterSnapshot', diff --git a/src/properties/logger.ts b/src/properties/logger.ts index 4226372c..cdba7848 100644 --- a/src/properties/logger.ts +++ b/src/properties/logger.ts @@ -702,4 +702,13 @@ export default { securityHubNotFound: (region: string): string => `Security Hub not found/disabled for region: ${region}`, fetchedSecurityHub: (region: string): string => `Security Hub found/enabled for region: ${region}`, fetchingSecurityHub: 'Fetching Security Hub data for this AWS account via the AWS SDK...', + /** + * OpsWorks + */ + fetchedOpsWorksStacks: (num: number): string => + `Fetched ${num} OpsWorks Stacks`, + fetchedOpsWorksInstances: (num: number): string => + `Fetched ${num} OpsWorks Instances`, + fetchedOpsWorksApps: (num: number): string => + `Fetched ${num} OpsWorks Apps`, } diff --git a/src/services/ec2/schema.graphql b/src/services/ec2/schema.graphql index 7ae9be65..1b931566 100644 --- a/src/services/ec2/schema.graphql +++ b/src/services/ec2/schema.graphql @@ -116,6 +116,7 @@ type awsEc2 implements awsBaseService @key(fields: "arn") { iamInstanceProfile: [awsIamInstanceProfile] @hasInverse(field: ec2Instances) iamRole: [awsIamRole] @hasInverse(field: ec2Instances) networkInterfaces: [awsNetworkInterface] @hasInverse(field: ec2Instance) + opsWorksInstances: [awsOpsWorksInstance] @hasInverse(field: ec2Instance) securityGroups: [awsSecurityGroup] @hasInverse(field: ec2Instance) subnets: [awsSubnet] @hasInverse(field: ec2Instances) systemsManagerInstance: [awsSystemsManagerInstance] @hasInverse(field: ec2Instance) diff --git a/src/services/opsworksApp/data.ts b/src/services/opsworksApp/data.ts new file mode 100644 index 00000000..fad59ddb --- /dev/null +++ b/src/services/opsworksApp/data.ts @@ -0,0 +1,117 @@ +import CloudGraph from '@cloudgraph/sdk' +import { AWSError } from 'aws-sdk/lib/error' +import { Config } from 'aws-sdk/lib/config' +import isEmpty from 'lodash/isEmpty' +import flatMap from 'lodash/flatMap' +import groupBy from 'lodash/groupBy' +import awsLoggerText from '../../properties/logger' +import { initTestEndpoint, setAwsRetryOptions } from '../../utils' +import AwsErrorLog from '../../utils/errorLog' +import { API_GATEWAY_CUSTOM_DELAY } from '../../config/constants' +import OpsWorks, { App, DescribeAppsResult } from 'aws-sdk/clients/opsworks' +import OpsWorksStack from '../opsworksStack' +import { RawAwsOpsWorksStack } from '../opsworksStack/data' + +const lt = { ...awsLoggerText } +const { logger } = CloudGraph +const serviceName = 'OpsWorks Stack' +const errorLog = new AwsErrorLog(serviceName) +const endpoint = initTestEndpoint(serviceName) +const customRetrySettings = setAwsRetryOptions({ + baseDelay: API_GATEWAY_CUSTOM_DELAY, +}) + +export const getOpsWorksAppsForRegion = async ( + opsWorks: OpsWorks, + StackId: string, +): Promise => + new Promise(async resolve => { + const listAllApps = (): void => { + try { + opsWorks.describeApps( + {StackId}, + (err: AWSError, data: DescribeAppsResult) => { + if (err) { + errorLog.generateAwsErrorLog({ + functionName: 'opsWorks:describeApps', + err, + }) + } + + if (isEmpty(data)) { + return resolve([]) + } + + const { Apps: apps = [] } = data || {} + + logger.debug(lt.fetchedOpsWorksApps(apps.length)) + + resolve(apps) + } + ); + } catch (error) { + resolve([]) + } + } + listAllApps() + }) + +export interface RawAwsOpsWorksApp extends Omit { + region: string + account +} + +export default async ({ + regions, + config, + account, +}: { + account: string + regions: string + config: Config +}): Promise<{ + [region: string]: RawAwsOpsWorksApp[] +}> => + new Promise(async resolve => { + const opsWorksResult: RawAwsOpsWorksApp[] = [] + const opsWorksStackClass = new OpsWorksStack({ logger: CloudGraph.logger }) + const stacksResult = await opsWorksStackClass.getData({ + ...config, + regions, + }) + const opsWorksStacks: RawAwsOpsWorksStack[] = flatMap(stacksResult) + + + const regionPromises = regions.split(',').map(region => { + const opsWorks = new OpsWorks({ + ...config, + region, + endpoint, + ...customRetrySettings, + }) + + return new Promise(async resolveOpsWorksData => { + // Get OpsWorks Apps Data + opsWorksStacks.map(async ({StackId}: RawAwsOpsWorksStack) => { + const opsWorksApps = await getOpsWorksAppsForRegion(opsWorks, StackId) + + if (!isEmpty(opsWorksApps)) { + for (const app of opsWorksApps) { + opsWorksResult.push({ + ...app, + region, + account, + }) + } + } + }) + + resolveOpsWorksData() + }) + }) + + await Promise.all(regionPromises) + errorLog.reset() + + resolve(groupBy(opsWorksResult, 'region')) + }) diff --git a/src/services/opsworksApp/format.ts b/src/services/opsworksApp/format.ts new file mode 100644 index 00000000..6658644b --- /dev/null +++ b/src/services/opsworksApp/format.ts @@ -0,0 +1,89 @@ +import { generateUniqueId } from '@cloudgraph/sdk' + +import { RawAwsOpsWorksApp } from './data' +import { AwsOpsWorksApp } from '../../types/generated' +import { opsworksAppArn } from '../../utils/generateArns' + +export default ({ + service, + account: accountId, + region, +}: { + service: RawAwsOpsWorksApp + account: string + region: string +}): AwsOpsWorksApp => { + const { + AppId: appId, + StackId: stackId, + Shortname: shortname, + Name: name, + Description: description, + DataSources: dataSources, + Type: type, + AppSource: appSource, + Domains: domains, + EnableSsl: enableSsl, + SslConfiguration: sslConfiguration, + Attributes: attributes, + CreatedAt: createdAt, + Environment: environment, + } = service + + const arn = opsworksAppArn({ region, account: accountId, name: name }) + + return { + id: appId, + accountId, + arn, + region, + stackId, + shortname, + name, + description, + dataSources: dataSources.map(ds => ({ + id: generateUniqueId({ + arn, + ...ds, + }), + type: ds.Type, + arn: ds.Arn, + databaseName: ds.DatabaseName, + })), + type, + appSource: { + type: appSource?.Type, + url: appSource?.Url, + username: appSource?.Username, + password: appSource?.Password, + sshKey: appSource?.SshKey, + revision: appSource?.Revision, + }, + domains, + enableSsl, + sslConfiguration: { + certificate: sslConfiguration?.Certificate, + privateKey: sslConfiguration?.PrivateKey, + chain: sslConfiguration?.Chain, + }, + attributes: Object.keys(attributes).map(key => ({ + id: generateUniqueId({ + arn, + key, + value: attributes[key], + }), + key, + value: attributes[key], + })), + createdAt, + environment: environment.map(e => ({ + id: generateUniqueId({ + arn, + ...e, + }), + key: e.Key, + value: e.Value, + secure: e.Secure, + })) + } +} diff --git a/src/services/opsworksApp/index.ts b/src/services/opsworksApp/index.ts new file mode 100644 index 00000000..b0303d13 --- /dev/null +++ b/src/services/opsworksApp/index.ts @@ -0,0 +1,13 @@ +import { Service } from '@cloudgraph/sdk' +import BaseService from '../base' +import format from './format' +import getData from './data' +import mutation from './mutation' + +export default class OpsWorksApp extends BaseService implements Service { + format = format.bind(this) + + getData = getData.bind(this) + + mutation = mutation +} \ No newline at end of file diff --git a/src/services/opsworksApp/mutation.ts b/src/services/opsworksApp/mutation.ts new file mode 100644 index 00000000..763ced01 --- /dev/null +++ b/src/services/opsworksApp/mutation.ts @@ -0,0 +1,5 @@ +export default `mutation($input: [AddawsOpsWorksAppInput!]!) { + addawsOpsWorksApp(input: $input, upsert: true) { + numUids + } +}` \ No newline at end of file diff --git a/src/services/opsworksApp/schema.graphql b/src/services/opsworksApp/schema.graphql new file mode 100644 index 00000000..1e5e9501 --- /dev/null +++ b/src/services/opsworksApp/schema.graphql @@ -0,0 +1,76 @@ +type awsOpsWorksAppDataSource + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + id: String! @id @search(by: [hash]) + type: String @search(by: [hash, regexp]) + arn: String @search(by: [hash, regexp]) + databaseName: String @search(by: [hash, regexp]) +} + +type awsOpsWorksAppSource + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + type: String @search(by: [hash, regexp]) + url: String @search(by: [hash, regexp]) + username: String @search(by: [hash, regexp]) + password: String @search(by: [hash, regexp]) + sshKey: String @search(by: [hash, regexp]) + revision: String @search(by: [hash, regexp]) +} + +type awsOpsWorksAppSslConfiguration + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + certificate: String @search(by: [hash, regexp]) + privateKey: String @search(by: [hash, regexp]) + chain: String @search(by: [hash, regexp]) +} + +type awsOpsWorksAppAttribute + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + id: String! @id @search(by: [hash]) + key: String @search(by: [hash, regexp]) + value: String @search(by: [hash, regexp]) +} + +type awsOpsWorksAppEnvironment + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + id: String! @id @search(by: [hash]) + key: String @search(by: [hash, regexp]) + value: String @search(by: [hash, regexp]) + secure: Boolean @search +} + +type awsOpsWorksApp implements awsBaseService @key(fields: "arn") { + stackId: String @search(by: [hash, regexp]) + shortname: String @search(by: [hash, regexp]) + name: String @search(by: [hash, regexp]) + description: String @search(by: [hash, regexp]) + dataSources: [awsOpsWorksAppDataSource] + type: String @search(by: [hash, regexp]) + appSource: awsOpsWorksAppSource + domains: [String] @search(by: [hash, regexp]) + enableSsl: Boolean @search + sslConfiguration: awsOpsWorksAppSslConfiguration + attributes: [awsOpsWorksAppAttribute] + createdAt: String @search(by: [hash, regexp]) + environment: [awsOpsWorksAppEnvironment] + opsWorksStacks: [awsOpsWorksStack] @hasInverse(field: opsWorksApps) +} diff --git a/src/services/opsworksInstance/connections.ts b/src/services/opsworksInstance/connections.ts new file mode 100644 index 00000000..aad5b384 --- /dev/null +++ b/src/services/opsworksInstance/connections.ts @@ -0,0 +1,74 @@ +import { ServiceConnection } from '@cloudgraph/sdk' + +import { isEmpty } from 'lodash' +import services from '../../enums/services' +import { RawAwsOpsWorksInstance } from './data' +import { RawAwsOpsWorksApp } from '../opsworksApp/data' + + +export default ({ + service: opsworksInstance, + data, + region, +}: { + service: RawAwsOpsWorksInstance + data: Array<{ name: string; data: { [property: string]: any[] } }> + region: string +}): { + [property: string]: ServiceConnection[] +} => { + const { + InstanceId, + SubnetId, + SecurityGroupIds: securityGroupIds = [], + Ec2InstanceId, + } = opsworksInstance + const connections: ServiceConnection[] = [] + + /** + * Find EC2 Instances + * related to this Auto Scaling Group + */ + const ec2Instances = data.find(({ name }) => name === services.ec2Instance) + if (ec2Instances?.data?.[region]) { + const ec2InstanceInRegion = ec2Instances.data[region].find(instance => + instance.InstanceId === Ec2InstanceId + ) + + if (ec2InstanceInRegion) { + connections.push({ + id: Ec2InstanceId, + resourceType: services.ec2Instance, + relation: 'child', + field: 'ec2Instance', + }) + } + } + + /** + * Add subnets + */ + connections.push({ + id: SubnetId, + resourceType: services.subnet, + relation: 'child', + field: 'subnet', + }) + + /** + * Add Security Groups + */ + connections.push( + ...securityGroupIds.map(sg => ({ + id: sg, + resourceType: services.sg, + relation: 'child', + field: 'securityGroups', + })) + ) + + const natResult = { + [InstanceId]: connections, + } + return natResult +} diff --git a/src/services/opsworksInstance/data.ts b/src/services/opsworksInstance/data.ts new file mode 100644 index 00000000..a3b3aa4e --- /dev/null +++ b/src/services/opsworksInstance/data.ts @@ -0,0 +1,116 @@ +import CloudGraph from '@cloudgraph/sdk' +import { AWSError } from 'aws-sdk/lib/error' +import { Config } from 'aws-sdk/lib/config' +import isEmpty from 'lodash/isEmpty' +import groupBy from 'lodash/groupBy' +import awsLoggerText from '../../properties/logger' +import { initTestEndpoint, setAwsRetryOptions } from '../../utils' +import AwsErrorLog from '../../utils/errorLog' +import { API_GATEWAY_CUSTOM_DELAY } from '../../config/constants' +import OpsWorks, { DescribeInstancesResult, Instance } from 'aws-sdk/clients/opsworks' +import OpsWorksStack from '../opsworksStack' +import { RawAwsOpsWorksStack } from '../opsworksStack/data' +import { flatMap } from 'lodash' + +const lt = { ...awsLoggerText } +const { logger } = CloudGraph +const serviceName = 'OpsWorks Instances' +const errorLog = new AwsErrorLog(serviceName) +const endpoint = initTestEndpoint(serviceName) +const customRetrySettings = setAwsRetryOptions({ + baseDelay: API_GATEWAY_CUSTOM_DELAY, +}) + +export const getOpsWorksInstancesForRegion = async ( + opsWorks: OpsWorks, + StackId: string, +): Promise => + new Promise(async resolve => { + const listAllInstances = (): void => { + try { + opsWorks.describeInstances( + {StackId}, + (err: AWSError, data: DescribeInstancesResult) => { + if (err) { + errorLog.generateAwsErrorLog({ + functionName: 'opsWorks:describeInstances', + err, + }) + } + + if (isEmpty(data)) { + return resolve([]) + } + + const { Instances: instances = [] } = data || {} + + logger.debug(lt.fetchedOpsWorksInstances(instances.length)) + + resolve(instances) + } + ); + } catch (error) { + resolve([]) + } + } + listAllInstances() + }) + +export interface RawAwsOpsWorksInstance extends Omit { + region: string + account +} + +export default async ({ + regions, + config, + account, +}: { + account: string + regions: string + config: Config +}): Promise<{ + [region: string]: RawAwsOpsWorksInstance[] +}> => + new Promise(async resolve => { + const opsWorksResult: RawAwsOpsWorksInstance[] = [] + const opsWorksStackClass = new OpsWorksStack({ logger: CloudGraph.logger }) + const stacksResult = await opsWorksStackClass.getData({ + ...config, + regions, + }) + const opsWorksStacks: RawAwsOpsWorksStack[] = flatMap(stacksResult) + + const regionPromises = regions.split(',').map(region => { + const opsWorks = new OpsWorks({ + ...config, + region, + endpoint, + ...customRetrySettings, + }) + + return new Promise(async resolveOpsWorksData => { + // Get OpsWorks Instance Data + opsWorksStacks.map(async ({StackId}: RawAwsOpsWorksStack) => { + const opsWorksInstances = await getOpsWorksInstancesForRegion(opsWorks, StackId) + + if (!isEmpty(opsWorksInstances)) { + for (const instance of opsWorksInstances) { + opsWorksResult.push({ + ...instance, + region, + account, + }) + } + } + }) + + resolveOpsWorksData() + }) + }) + + await Promise.all(regionPromises) + errorLog.reset() + + resolve(groupBy(opsWorksResult, 'region')) + }) diff --git a/src/services/opsworksInstance/format.ts b/src/services/opsworksInstance/format.ts new file mode 100644 index 00000000..e0536cf3 --- /dev/null +++ b/src/services/opsworksInstance/format.ts @@ -0,0 +1,124 @@ +import { generateUniqueId } from '@cloudgraph/sdk' + +import { RawAwsOpsWorksInstance } from './data' +import { AwsOpsWorksInstance } from '../../types/generated' + +export default ({ + service, + account: accountId, + region, +}: { + service: RawAwsOpsWorksInstance + account: string + region: string +}): AwsOpsWorksInstance => { + const { + AgentVersion: agentVersion, + AmiId: amiId, + Architecture: architecture, + Arn: arn, + AutoScalingType: autoScalingType, + AvailabilityZone: availabilityZone, + BlockDeviceMappings: blockDeviceMappings, + CreatedAt: createdAt, + EbsOptimized: ebsOptimized, + Ec2InstanceId: ec2InstanceId, + EcsClusterArn: ecsClusterArn, + EcsContainerInstanceArn: ecsContainerInstanceArn, + ElasticIp: elasticIp, + Hostname: hostname, + InfrastructureClass: infrastructureClass, + InstallUpdatesOnBoot: installUpdatesOnBoot, + InstanceId: instanceId, + InstanceProfileArn: instanceProfileArn, + InstanceType: instanceType, + LastServiceErrorId: lastServiceErrorId, + LayerIds: layerIds, + Os: os, + Platform: platform, + PrivateDns: privateDns, + PrivateIp: privateIp, + PublicDns: publicDns, + PublicIp: publicIp, + RegisteredBy: registeredBy, + ReportedAgentVersion: reportedAgentVersion, + ReportedOs: reportedOs, + RootDeviceType: rootDeviceType, + RootDeviceVolumeId: rootDeviceVolumeId, + SecurityGroupIds: securityGroupIds, + SshHostDsaKeyFingerprint: sshHostDsaKeyFingerprint, + SshHostRsaKeyFingerprint: sshHostRsaKeyFingerprint, + SshKeyName: sshKeyName, + StackId: stackId, + Status: status, + SubnetId: subnetId, + Tenancy: tenancy, + VirtualizationType: virtualizationType, + } = service + + return { + id: instanceId, + accountId, + arn, + region, + agentVersion, + amiId, + architecture, + autoScalingType, + availabilityZone, + blockDeviceMappings: blockDeviceMappings.map(bl => ({ + id: generateUniqueId({ + arn, + ...bl, + }), + deviceName: bl.DeviceName, + noDevice: bl.NoDevice, + virtualName: bl.VirtualName, + ebs: { + snapshotId: bl.Ebs?.SnapshotId, + iops: bl.Ebs?.Iops, + volumeSize: bl.Ebs?.VolumeSize, + volumeType: bl.Ebs?.VolumeType, + deleteOnTermination: bl.Ebs?.DeleteOnTermination, + }, + })) || [], + createdAt, + ebsOptimized, + ec2InstanceId, + ecsClusterArn, + ecsContainerInstanceArn, + elasticIp, + hostname, + infrastructureClass, + installUpdatesOnBoot, + instanceId, + instanceProfileArn, + instanceType, + lastServiceErrorId, + layerIds, + os, + platform, + privateDns, + privateIp, + publicDns, + publicIp, + registeredBy, + reportedAgentVersion, + reportedOs: { + family: reportedOs?.Family, + name: reportedOs?.Name, + version: reportedOs?.Version, + }, + rootDeviceType, + rootDeviceVolumeId, + securityGroupIds, + sshHostDsaKeyFingerprint, + sshHostRsaKeyFingerprint, + sshKeyName, + stackId, + status, + subnetId, + tenancy, + virtualizationType, + } +} diff --git a/src/services/opsworksInstance/index.ts b/src/services/opsworksInstance/index.ts new file mode 100644 index 00000000..952c3c3e --- /dev/null +++ b/src/services/opsworksInstance/index.ts @@ -0,0 +1,16 @@ +import { Service } from '@cloudgraph/sdk' +import BaseService from '../base' +import format from './format' +import getConnections from './connections'; +import getData from './data' +import mutation from './mutation' + +export default class OpsWorksInstance extends BaseService implements Service { + format = format.bind(this) + + getConnections = getConnections.bind(this) + + getData = getData.bind(this) + + mutation = mutation +} \ No newline at end of file diff --git a/src/services/opsworksInstance/mutation.ts b/src/services/opsworksInstance/mutation.ts new file mode 100644 index 00000000..923f9425 --- /dev/null +++ b/src/services/opsworksInstance/mutation.ts @@ -0,0 +1,5 @@ +export default `mutation($input: [AddawsOpsWorksInstanceInput!]!) { + addawsOpsWorksInstance(input: $input, upsert: true) { + numUids + } +}` \ No newline at end of file diff --git a/src/services/opsworksInstance/schema.graphql b/src/services/opsworksInstance/schema.graphql new file mode 100644 index 00000000..0c790bba --- /dev/null +++ b/src/services/opsworksInstance/schema.graphql @@ -0,0 +1,83 @@ +type awsOpsWorksInstanceBlockDeviceMappingEbs + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + snapshotId: String @search(by: [hash, regexp]) + iops: Int @search + volumeSize: Int @search + volumeType: String @search(by: [hash, regexp]) + deleteOnTermination: Boolean @search +} + +type awsOpsWorksInstanceBlockDeviceMapping + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + id: String! @id @search(by: [hash]) + deviceName: String @search(by: [hash, regexp]) + noDevice: String @search(by: [hash, regexp]) + virtualName: String @search(by: [hash, regexp]) + ebs: awsOpsWorksInstanceBlockDeviceMappingEbs +} + +type awsOpsWorksInstanceReportedOs + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + family: String @search(by: [hash, regexp]) + name: String @search(by: [hash, regexp]) + version: String @search(by: [hash, regexp]) +} + +type awsOpsWorksInstance implements awsBaseService @key(fields: "arn") { + agentVersion: String @search(by: [hash, regexp]) + amiId: String @search(by: [hash, regexp]) + architecture: String @search(by: [hash, regexp]) + autoScalingType: String @search(by: [hash, regexp]) + availabilityZone: String @search(by: [hash, regexp]) + blockDeviceMappings: [awsOpsWorksInstanceBlockDeviceMapping] + createdAt: String @search(by: [hash, regexp]) + ebsOptimized: Boolean @search + ec2InstanceId: String @search(by: [hash, regexp]) + ecsClusterArn: String @search(by: [hash, regexp]) + ecsContainerInstanceArn: String @search(by: [hash, regexp]) + elasticIp: String @search(by: [hash, regexp]) + hostname: String @search(by: [hash, regexp]) + infrastructureClass: String @search(by: [hash, regexp]) + installUpdatesOnBoot: Boolean @search + instanceId: String @search(by: [hash, regexp]) + instanceProfileArn: String @search(by: [hash, regexp]) + instanceType: String @search(by: [hash, regexp]) + lastServiceErrorId: String @search(by: [hash, regexp]) + layerIds: [String] @search(by: [hash, regexp]) + os: String @search(by: [hash, regexp]) + platform: String @search(by: [hash, regexp]) + privateDns: String @search(by: [hash, regexp]) + privateIp: String @search(by: [hash, regexp]) + publicDns: String @search(by: [hash, regexp]) + publicIp: String @search(by: [hash, regexp]) + registeredBy: String @search(by: [hash, regexp]) + reportedAgentVersion: String @search(by: [hash, regexp]) + reportedOs: awsOpsWorksInstanceReportedOs + rootDeviceType: String @search(by: [hash, regexp]) + rootDeviceVolumeId: String @search(by: [hash, regexp]) + securityGroupIds: [String] @search(by: [hash, regexp]) + sshHostDsaKeyFingerprint: String @search(by: [hash, regexp]) + sshHostRsaKeyFingerprint: String @search(by: [hash, regexp]) + sshKeyName: String @search(by: [hash, regexp]) + stackId: String @search(by: [hash, regexp]) + status: String @search(by: [hash, regexp]) + subnetId: String @search(by: [hash, regexp]) + tenancy: String @search(by: [hash, regexp]) + virtualizationType: String @search(by: [hash, regexp]) + opsWorksStacks: [awsOpsWorksStack] @hasInverse(field: opsWorksInstances) + subnets: [awsSubnet] @hasInverse(field: opsWorksInstances) + securityGroups: [awsSecurityGroup] @hasInverse(field: opsWorksInstances) + ec2Instance: [awsEc2] @hasInverse(field: opsWorksInstances) +} diff --git a/src/services/opsworksStack/connections.ts b/src/services/opsworksStack/connections.ts new file mode 100644 index 00000000..8c56d4b4 --- /dev/null +++ b/src/services/opsworksStack/connections.ts @@ -0,0 +1,78 @@ +import { ServiceConnection } from '@cloudgraph/sdk' + +import services from '../../enums/services' +import { RawAwsOpsWorksStack } from './data' +import { RawAwsOpsWorksInstance } from '../opsworksInstance/data' +import { RawAwsOpsWorksApp } from '../opsworksApp/data' + + +export default ({ + service: opsworksStack, + data, + region, +}: { + service: RawAwsOpsWorksStack + data: Array<{ name: string; data: { [property: string]: any[] } }> + region: string +}): { + [property: string]: ServiceConnection[] +} => { + const { + StackId, DefaultSubnetId, VpcId + } = opsworksStack + const connections: ServiceConnection[] = [] + + /** + * Find any opsworks Instances + * related to this opsworks Stack + */ + const opsworksInstances = data.find(({ name }) => name === services.opsWorksInstance) + if (opsworksInstances?.data?.[region]) { + const dataAtRegion: RawAwsOpsWorksInstance[] = opsworksInstances.data[region].filter( + ({ StackId: stackId }: RawAwsOpsWorksInstance) => stackId === StackId + ) + for (const opsWorksInstance of dataAtRegion) { + connections.push({ + id: opsWorksInstance.Arn, + resourceType: services.opsWorksInstance, + relation: 'child', + field: 'opsWorksInstances', + }) + } + } + + /** + * Find any opsworks App + * related to this opsworks Stack + */ + const opsworksApp = data.find(({ name }) => name === services.opsWorksApp) + if (opsworksApp?.data?.[region]) { + const dataAtRegion: RawAwsOpsWorksApp[] = opsworksApp.data[region].filter( + ({ StackId: stackId }: RawAwsOpsWorksApp) => + stackId === StackId + ) + for (const opsWorksInstance of dataAtRegion) { + connections.push({ + id: opsWorksInstance.AppId, + resourceType: services.opsWorksApp, + relation: 'child', + field: 'opsWorksApps', + }) + } + } + + /** + * Add subnets + */ + connections.push({ + id: DefaultSubnetId, + resourceType: services.subnet, + relation: 'child', + field: 'subnet', + }) + + const natResult = { + [StackId]: connections, + } + return natResult +} diff --git a/src/services/opsworksStack/data.ts b/src/services/opsworksStack/data.ts new file mode 100644 index 00000000..df58ceff --- /dev/null +++ b/src/services/opsworksStack/data.ts @@ -0,0 +1,103 @@ +import CloudGraph from '@cloudgraph/sdk' +import { AWSError } from 'aws-sdk/lib/error' +import { Config } from 'aws-sdk/lib/config' +import isEmpty from 'lodash/isEmpty' +import groupBy from 'lodash/groupBy' +import awsLoggerText from '../../properties/logger' +import { initTestEndpoint, setAwsRetryOptions } from '../../utils' +import AwsErrorLog from '../../utils/errorLog' +import { API_GATEWAY_CUSTOM_DELAY } from '../../config/constants' +import OpsWorks, { DescribeStacksResult, Stack } from 'aws-sdk/clients/opsworks' + +const lt = { ...awsLoggerText } +const { logger } = CloudGraph +const serviceName = 'OpsWorks Stack' +const errorLog = new AwsErrorLog(serviceName) +const endpoint = initTestEndpoint(serviceName) +const customRetrySettings = setAwsRetryOptions({ + baseDelay: API_GATEWAY_CUSTOM_DELAY, +}) + +export const getOpsWorksStacksForRegion = async ( + opsWorks: OpsWorks +): Promise => + new Promise(async resolve => { + const listAllStacks = (): void => { + try { + opsWorks.describeStacks( + (err: AWSError, data: DescribeStacksResult) => { + if (err) { + errorLog.generateAwsErrorLog({ + functionName: 'opsWorks:describeStacks', + err, + }) + } + + if (isEmpty(data)) { + return resolve([]) + } + + const { Stacks: stacks = [] } = data || {} + + logger.debug(lt.fetchedOpsWorksStacks(stacks.length)) + resolve(stacks) + } + ); + } catch (error) { + resolve([]) + } + } + listAllStacks() + }) + +export interface RawAwsOpsWorksStack extends Omit { + region: string + account +} + +export default async ({ + regions, + config, + account, +}: { + account: string + regions: string + config: Config +}): Promise<{ + [region: string]: RawAwsOpsWorksStack[] +}> => + new Promise(async resolve => { + const opsWorksResult: RawAwsOpsWorksStack[] = [] + + const regionPromises = regions.split(',').map(region => { + const opsWorks = new OpsWorks({ + ...config, + region, + endpoint, + ...customRetrySettings, + }) + + return new Promise(async resolveOpsWorksData => { + // Get OpsWorks Stack Data + const opsWorksStacks = await getOpsWorksStacksForRegion(opsWorks) + + if (!isEmpty(opsWorksStacks)) { + for (const stack of opsWorksStacks) { + opsWorksResult.push({ + ...stack, + region, + // Tags: domain.Tags, + account, + }) + } + } + + resolveOpsWorksData() + }) + }) + + await Promise.all(regionPromises) + errorLog.reset() + + resolve(groupBy(opsWorksResult, 'region')) + }) diff --git a/src/services/opsworksStack/format.ts b/src/services/opsworksStack/format.ts new file mode 100644 index 00000000..40ba413a --- /dev/null +++ b/src/services/opsworksStack/format.ts @@ -0,0 +1,84 @@ +import { generateUniqueId } from '@cloudgraph/sdk' + +import { RawAwsOpsWorksStack } from './data' +import { AwsOpsWorksStack } from '../../types/generated' + +export default ({ + service, + account: accountId, + region, +}: { + service: RawAwsOpsWorksStack + account: string + region: string +}): AwsOpsWorksStack => { + const { + StackId: stackId, + Name: name, + Arn: arn, + VpcId: vpcId, + Attributes: attributes, + ServiceRoleArn: serviceRoleArn, + DefaultInstanceProfileArn: defaultInstanceProfileArn, + DefaultOs: defaultOs, + HostnameTheme: hostnameTheme, + DefaultAvailabilityZone: defaultAvailabilityZone, + DefaultSubnetId: defaultSubnetId, + CustomJson: customJson, + ConfigurationManager: configurationManager, + ChefConfiguration: chefConfiguration, + UseCustomCookbooks: useCustomCookbooks, + UseOpsworksSecurityGroups: useOpsworksSecurityGroups, + CustomCookbooksSource: customCookbooksSource, + DefaultSshKeyName: defaultSshKeyName, + CreatedAt: createdAt, + DefaultRootDeviceType: defaultRootDeviceType, + AgentVersion: agentVersion, + } = service + + return { + id: stackId, + accountId, + arn, + region, + vpcId, + attributes: Object.keys(attributes).map(key => ({ + id: generateUniqueId({ + arn, + key, + value: attributes[key], + }), + key, + value: attributes[key], + })), + serviceRoleArn, + defaultInstanceProfileArn, + defaultOs, + hostnameTheme, + defaultAvailabilityZone, + defaultSubnetId, + customJson, + configurationManager: { + name: configurationManager?.Name, + version: configurationManager?.Version, + }, + chefConfiguration: { + manageBerkshelf: chefConfiguration?.ManageBerkshelf, + berkshelfVersion: chefConfiguration?.BerkshelfVersion, + }, + useCustomCookbooks, + useOpsworksSecurityGroups, + customCookbooksSource: { + type: customCookbooksSource?.Type, + url: customCookbooksSource?.Url, + username: customCookbooksSource?.Username, + password: customCookbooksSource?.Password, + sshKey: customCookbooksSource?.SshKey, + revision: customCookbooksSource?.Revision, + }, + defaultSshKeyName, + createdAt, + defaultRootDeviceType, + agentVersion, + } +} diff --git a/src/services/opsworksStack/index.ts b/src/services/opsworksStack/index.ts new file mode 100644 index 00000000..96142b54 --- /dev/null +++ b/src/services/opsworksStack/index.ts @@ -0,0 +1,16 @@ +import { Service } from '@cloudgraph/sdk' +import BaseService from '../base' +import format from './format' +import getConnections from './connections'; +import getData from './data' +import mutation from './mutation' + +export default class OpsWorksStack extends BaseService implements Service { + format = format.bind(this) + + getConnections = getConnections.bind(this) + + getData = getData.bind(this) + + mutation = mutation +} \ No newline at end of file diff --git a/src/services/opsworksStack/mutation.ts b/src/services/opsworksStack/mutation.ts new file mode 100644 index 00000000..0fd6e053 --- /dev/null +++ b/src/services/opsworksStack/mutation.ts @@ -0,0 +1,5 @@ +export default `mutation($input: [AddawsOpsWorksStackInput!]!) { + addawsOpsWorksStack(input: $input, upsert: true) { + numUids + } +}` \ No newline at end of file diff --git a/src/services/opsworksStack/schema.graphql b/src/services/opsworksStack/schema.graphql new file mode 100644 index 00000000..36ec43f7 --- /dev/null +++ b/src/services/opsworksStack/schema.graphql @@ -0,0 +1,69 @@ +type awsOpsWorksStackAttribute + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + id: String! @id @search(by: [hash]) + key: String @search(by: [hash, regexp]) + value: String @search(by: [hash, regexp]) +} + +type awsOpsWorksStackConfigurationManager + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + name: String @search(by: [hash, regexp]) + version: String @search(by: [hash, regexp]) +} + +type awsOpsWorksStackChefConfiguration + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + manageBerkshelf: Boolean @search + berkshelfVersion: String @search(by: [hash, regexp]) +} + +type awsOpsWorksStackCustomCookbooksSource + @generate( + query: { get: false, query: false, aggregate: false } + mutation: { add: false, delete: false } + subscription: false + ) { + type: String @search(by: [hash, regexp]) + url: String @search(by: [hash, regexp]) + username: String @search(by: [hash, regexp]) + password: String @search(by: [hash, regexp]) + sshKey: String @search(by: [hash, regexp]) + revision: String @search(by: [hash, regexp]) +} + +type awsOpsWorksStack implements awsBaseService @key(fields: "arn") { + vpcId: String @search(by: [hash, regexp]) + attributes: [awsOpsWorksStackAttribute] + serviceRoleArn: String @search(by: [hash, regexp]) + defaultInstanceProfileArn: String @search(by: [hash, regexp]) + defaultOs: String @search(by: [hash, regexp]) + hostnameTheme: String @search(by: [hash, regexp]) + defaultAvailabilityZone: String @search(by: [hash, regexp]) + defaultSubnetId: String @search(by: [hash, regexp]) + customJson: String @search(by: [hash, regexp]) + configurationManager: awsOpsWorksStackConfigurationManager + chefConfiguration: awsOpsWorksStackChefConfiguration + useCustomCookbooks: Boolean @search + useOpsworksSecurityGroups: Boolean @search + customCookbooksSource: awsOpsWorksStackCustomCookbooksSource + defaultSshKeyName: String @search(by: [hash, regexp]) + createdAt: String @search(by: [hash, regexp]) + defaultRootDeviceType: String @search(by: [hash, regexp]) + agentVersion: String @search(by: [hash, regexp]) + opsWorksInstances: [awsOpsWorksInstance] @hasInverse(field: opsWorksStacks) + opsWorksApps: [awsOpsWorksApp] @hasInverse(field: opsWorksStacks) + subnets: [awsSubnet] @hasInverse(field: opsWorksStacks) + vpc: [awsVpc] @hasInverse(field: opsWorksStacks) +} diff --git a/src/services/securityGroup/schema.graphql b/src/services/securityGroup/schema.graphql index 48bf1666..8608c1a4 100644 --- a/src/services/securityGroup/schema.graphql +++ b/src/services/securityGroup/schema.graphql @@ -30,6 +30,7 @@ type awsSecurityGroup implements awsBaseService @key(fields: "id") { sageMakerNotebookInstances: [awsSageMakerNotebookInstance] @hasInverse(field: securityGroups) vpcEndpoints: [awsVpcEndpoint] @hasInverse(field: securityGroups) + opsWorksInstances: [awsOpsWorksInstance] @hasInverse(field: securityGroups) } type awsSgOutboundRule diff --git a/src/services/subnet/schema.graphql b/src/services/subnet/schema.graphql index b8cf400e..741aa533 100644 --- a/src/services/subnet/schema.graphql +++ b/src/services/subnet/schema.graphql @@ -34,4 +34,6 @@ type awsSubnet implements awsBaseService @key(fields: "id") { @hasInverse(field: subnet) rdsCluster: [awsRdsCluster] @hasInverse(field: subnets) vpcEndpoints: [awsVpcEndpoint] @hasInverse(field: subnets) + opsWorksStacks: [awsOpsWorksStack] @hasInverse(field: subnets) + opsWorksInstances: [awsOpsWorksInstance] @hasInverse(field: subnets) } diff --git a/src/services/vpc/schema.graphql b/src/services/vpc/schema.graphql index 0d742e73..f9013574 100644 --- a/src/services/vpc/schema.graphql +++ b/src/services/vpc/schema.graphql @@ -34,4 +34,5 @@ type awsVpc implements awsBaseService @key(fields: "id") { rdsClusterSnapshots: [awsRdsClusterSnapshot] @hasInverse(field: vpc) vpcPeeringConnection: [awsVpcPeeringConnection] @hasInverse(field: vpc) vpcEndpoints: [awsVpcEndpoint] @hasInverse(field: vpc) + opsWorksStacks: [awsOpsWorksStack] @hasInverse(field: vpc) } diff --git a/src/types/generated.ts b/src/types/generated.ts index 90ca09e6..5038d8c8 100644 --- a/src/types/generated.ts +++ b/src/types/generated.ts @@ -1584,6 +1584,7 @@ export type AwsEc2 = AwsBaseService & { metadatasecurityGroupIdsOptions?: Maybe>>; monitoring?: Maybe; networkInterfaces?: Maybe>>; + opsWorksInstances?: Maybe>>; placementGroup?: Maybe; platformDetails?: Maybe; primaryNetworkInterface?: Maybe; @@ -3652,6 +3653,177 @@ export type AwsNetworkInterfaceAttachment = { status?: Maybe; }; +export type AwsOpsWorksApp = AwsBaseService & { + appSource?: Maybe; + attributes?: Maybe>>; + createdAt?: Maybe; + dataSources?: Maybe>>; + description?: Maybe; + domains?: Maybe>>; + enableSsl?: Maybe; + environment?: Maybe>>; + name?: Maybe; + opsWorksStacks?: Maybe>>; + shortname?: Maybe; + sslConfiguration?: Maybe; + stackId?: Maybe; + type?: Maybe; +}; + +export type AwsOpsWorksAppAttribute = { + id: Scalars['String']; + key?: Maybe; + value?: Maybe; +}; + +export type AwsOpsWorksAppDataSource = { + arn?: Maybe; + databaseName?: Maybe; + id: Scalars['String']; + type?: Maybe; +}; + +export type AwsOpsWorksAppEnvironment = { + id: Scalars['String']; + key?: Maybe; + secure?: Maybe; + value?: Maybe; +}; + +export type AwsOpsWorksAppSource = { + password?: Maybe; + revision?: Maybe; + sshKey?: Maybe; + type?: Maybe; + url?: Maybe; + username?: Maybe; +}; + +export type AwsOpsWorksAppSslConfiguration = { + certificate?: Maybe; + chain?: Maybe; + privateKey?: Maybe; +}; + +export type AwsOpsWorksInstance = AwsBaseService & { + agentVersion?: Maybe; + amiId?: Maybe; + architecture?: Maybe; + autoScalingType?: Maybe; + availabilityZone?: Maybe; + blockDeviceMappings?: Maybe>>; + createdAt?: Maybe; + ebsOptimized?: Maybe; + ec2Instance?: Maybe>>; + ec2InstanceId?: Maybe; + ecsClusterArn?: Maybe; + ecsContainerInstanceArn?: Maybe; + elasticIp?: Maybe; + hostname?: Maybe; + infrastructureClass?: Maybe; + installUpdatesOnBoot?: Maybe; + instanceId?: Maybe; + instanceProfileArn?: Maybe; + instanceType?: Maybe; + lastServiceErrorId?: Maybe; + layerIds?: Maybe>>; + opsWorksStacks?: Maybe>>; + os?: Maybe; + platform?: Maybe; + privateDns?: Maybe; + privateIp?: Maybe; + publicDns?: Maybe; + publicIp?: Maybe; + registeredBy?: Maybe; + reportedAgentVersion?: Maybe; + reportedOs?: Maybe; + rootDeviceType?: Maybe; + rootDeviceVolumeId?: Maybe; + securityGroupIds?: Maybe>>; + securityGroups?: Maybe>>; + sshHostDsaKeyFingerprint?: Maybe; + sshHostRsaKeyFingerprint?: Maybe; + sshKeyName?: Maybe; + stackId?: Maybe; + status?: Maybe; + subnetId?: Maybe; + subnets?: Maybe>>; + tenancy?: Maybe; + virtualizationType?: Maybe; +}; + +export type AwsOpsWorksInstanceBlockDeviceMapping = { + deviceName?: Maybe; + ebs?: Maybe; + id: Scalars['String']; + noDevice?: Maybe; + virtualName?: Maybe; +}; + +export type AwsOpsWorksInstanceBlockDeviceMappingEbs = { + deleteOnTermination?: Maybe; + iops?: Maybe; + snapshotId?: Maybe; + volumeSize?: Maybe; + volumeType?: Maybe; +}; + +export type AwsOpsWorksInstanceReportedOs = { + family?: Maybe; + name?: Maybe; + version?: Maybe; +}; + +export type AwsOpsWorksStack = AwsBaseService & { + agentVersion?: Maybe; + attributes?: Maybe>>; + chefConfiguration?: Maybe; + configurationManager?: Maybe; + createdAt?: Maybe; + customCookbooksSource?: Maybe; + customJson?: Maybe; + defaultAvailabilityZone?: Maybe; + defaultInstanceProfileArn?: Maybe; + defaultOs?: Maybe; + defaultRootDeviceType?: Maybe; + defaultSshKeyName?: Maybe; + defaultSubnetId?: Maybe; + hostnameTheme?: Maybe; + opsWorksApps?: Maybe>>; + opsWorksInstances?: Maybe>>; + serviceRoleArn?: Maybe; + subnets?: Maybe>>; + useCustomCookbooks?: Maybe; + useOpsworksSecurityGroups?: Maybe; + vpc?: Maybe>>; + vpcId?: Maybe; +}; + +export type AwsOpsWorksStackAttribute = { + id: Scalars['String']; + key?: Maybe; + value?: Maybe; +}; + +export type AwsOpsWorksStackChefConfiguration = { + berkshelfVersion?: Maybe; + manageBerkshelf?: Maybe; +}; + +export type AwsOpsWorksStackConfigurationManager = { + name?: Maybe; + version?: Maybe; +}; + +export type AwsOpsWorksStackCustomCookbooksSource = { + password?: Maybe; + revision?: Maybe; + sshKey?: Maybe; + type?: Maybe; + url?: Maybe; + username?: Maybe; +}; + export type AwsOptionalService = { accountId?: Maybe; arn?: Maybe; @@ -4082,6 +4254,7 @@ export type AwsSecurityGroup = AwsBaseService & { managedAirflows?: Maybe>>; name?: Maybe; networkInterfaces?: Maybe>>; + opsWorksInstances?: Maybe>>; outboundRuleCount?: Maybe; outboundRules?: Maybe>>; owner?: Maybe; @@ -4223,6 +4396,8 @@ export type AwsSubnet = AwsBaseService & { nacls?: Maybe>>; natGateway?: Maybe>>; networkInterface?: Maybe>>; + opsWorksInstances?: Maybe>>; + opsWorksStacks?: Maybe>>; rdsCluster?: Maybe>>; rdsDbInstance?: Maybe>>; routeTable?: Maybe>>; @@ -4486,6 +4661,7 @@ export type AwsVpc = AwsBaseService & { nacls?: Maybe>>; natGateways?: Maybe>>; networkInterfaces?: Maybe>>; + opsWorksStacks?: Maybe>>; rdsClusterSnapshots?: Maybe>>; rdsDbInstances?: Maybe>>; redshiftClusters?: Maybe>>; diff --git a/src/utils/generateArns.ts b/src/utils/generateArns.ts index fc7e921a..9ab96c51 100644 --- a/src/utils/generateArns.ts +++ b/src/utils/generateArns.ts @@ -344,4 +344,15 @@ export const transitGatewayRouteTableArn = ({ region: string account: string id: string -}): string => `arn:aws:ec2:${region}:${account}:transit-gateway-routetable/${id}` \ No newline at end of file +}): string => `arn:aws:ec2:${region}:${account}:transit-gateway-routetable/${id}` + +export const opsworksAppArn = ({ + region, + account, + name, +}: { + region: string + account: string + name: string +}): string => + `arn:aws:opsworks:${region}:${account}:app/${name}` From 5cb33129fc9b11cbd9eab1cff09624a7d10fb0e7 Mon Sep 17 00:00:00 2001 From: James Zhou Date: Wed, 14 Jun 2023 15:23:12 -0500 Subject: [PATCH 2/3] using appId instead of name to build the arn --- src/services/opsworksApp/format.ts | 2 +- src/utils/generateArns.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/opsworksApp/format.ts b/src/services/opsworksApp/format.ts index 6658644b..7f9e9da5 100644 --- a/src/services/opsworksApp/format.ts +++ b/src/services/opsworksApp/format.ts @@ -30,7 +30,7 @@ export default ({ Environment: environment, } = service - const arn = opsworksAppArn({ region, account: accountId, name: name }) + const arn = opsworksAppArn({ region, account: accountId, appId: appId }) return { id: appId, diff --git a/src/utils/generateArns.ts b/src/utils/generateArns.ts index 9ab96c51..b3eaf416 100644 --- a/src/utils/generateArns.ts +++ b/src/utils/generateArns.ts @@ -349,10 +349,10 @@ export const transitGatewayRouteTableArn = ({ export const opsworksAppArn = ({ region, account, - name, + appId, }: { region: string account: string - name: string + appId: string }): string => - `arn:aws:opsworks:${region}:${account}:app/${name}` + `arn:aws:opsworks:${region}:${account}:app/${appId}` From 2b936825876f8bd6917757818b56c71aa816ad36 Mon Sep 17 00:00:00 2001 From: "mariano.pizarro" Date: Wed, 15 Nov 2023 11:07:28 -0300 Subject: [PATCH 3/3] feat(services): update account connections --- src/services/account/schema.graphql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/account/schema.graphql b/src/services/account/schema.graphql index f44c9300..78136c3e 100644 --- a/src/services/account/schema.graphql +++ b/src/services/account/schema.graphql @@ -86,6 +86,9 @@ type awsAccount implements awsOptionalService @key(fields: "id") { nacl: [awsNetworkAcl] natGateway: [awsNatGateway] networkInterfaces: [awsNetworkInterface] + opsWorksApps: [awsOpsWorksStack] + opsWorksStacks: [awsOpsWorksStack] + opsWorksInstances: [awsOpsWorksInstance] organizations: [awsOrganization] rdsClusters: [awsRdsCluster] rdsGlobalCluster: [awsRdsGlobalCluster]