Skip to content

Commit b486ae4

Browse files
committed
feat(services): add new services systemsManager instance and document
1 parent b77f9bc commit b486ae4

20 files changed

Lines changed: 671 additions & 25 deletions

File tree

src/enums/schemasMap.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ export default {
8989
[services.secretsManager]: 'awsSecretsManager',
9090
[services.ses]: 'awsSes',
9191
[services.sns]: 'awsSns',
92+
[services.systemsManagerInstance]: 'awsSystemsManagerInstance',
93+
[services.systemsManagerDocument]: 'awsSystemsManagerDocument',
9294
[services.transitGateway]: 'awsTransitGateway',
9395
[services.transitGatewayAttachment]: 'awsTransitGatewayAttachment',
9496
[services.vpnConnection]: 'awsVpnConnection',

src/enums/serviceMap.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ import GuardDutyDetector from '../services/guardDutyDetector'
9090
import ElasticSearchDomain from '../services/elasticSearchDomain'
9191
import DmsReplicationInstance from '../services/dmsReplicationInstance'
9292
import SageMakerNotebookInstance from '../services/sageMakerNotebookInstance'
93+
import SystemsManagerInstance from '../services/systemsManagerInstance'
94+
import SystemsManagerDocument from '../services/systemsManagerDocument'
9395

9496
/**
9597
* serviceMap is an object that contains all currently supported services for AWS
@@ -186,5 +188,7 @@ export default {
186188
[services.vpnConnection]: VpnConnection,
187189
[services.organization]: Organization,
188190
[services.wafV2WebAcl]: WafV2WebAcl,
191+
[services.systemsManagerInstance]: SystemsManagerInstance,
192+
[services.systemsManagerDocument]: SystemsManagerDocument,
189193
tag: AwsTag,
190194
}

src/enums/services.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ export default {
8383
sns: 'sns',
8484
sqs: 'sqs',
8585
subnet: 'subnet',
86+
systemsManagerInstance: 'systemsManagerInstance',
87+
systemsManagerDocument: 'systemsManagerDocument',
8688
transitGateway: 'transitGateway',
8789
transitGatewayAttachment: 'transitGatewayAttachment',
8890
vpc: 'vpc',

src/services/iamRole/connections.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { glueJobArn } from '../../utils/generateArns'
1414
import { RawAwsManagedAirflow } from '../managedAirflow/data'
1515
import { RawAwsGuardDutyDetector } from '../guardDutyDetector/data'
1616
import { RawAwsSageMakerNotebookInstance } from '../sageMakerNotebookInstance/data'
17+
import { RawAwsSystemsManagerInstance } from '../systemsManagerInstance/data'
1718

1819
/**
1920
* IAM Role
@@ -188,6 +189,28 @@ export default ({
188189
})
189190
}
190191
}
192+
/**
193+
* Find any systemsManagerInstance related data
194+
*/
195+
const systemsManagerInstances = data.find(
196+
({ name }) => name === services.systemsManagerInstance
197+
)
198+
if (systemsManagerInstances?.data?.[region]) {
199+
const dataAtRegion: RawAwsSystemsManagerInstance[] = systemsManagerInstances.data[
200+
region
201+
].filter(
202+
({ IamRole }: RawAwsSystemsManagerInstance) =>
203+
IamRole === role.Arn
204+
)
205+
for (const instance of dataAtRegion) {
206+
connections.push({
207+
id: instance.InstanceId,
208+
resourceType: services.systemsManagerInstance,
209+
relation: 'child',
210+
field: 'systemManagerInstances',
211+
})
212+
}
213+
}
191214

192215
/**
193216
* Find any sageMakerNotebookInstance related data

src/services/iamRole/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ type awsIamRole @key(fields: "id") {
2121
managedAirflows: [awsManagedAirflow] @hasInverse(field: iamRoles)
2222
guardDutyDetectors: [awsGuardDutyDetector] @hasInverse(field: iamRole)
2323
sageMakerNotebookInstances: [awsSageMakerNotebookInstance] @hasInverse(field: iamRole)
24+
systemManagerInstances: [awsSystemsManagerInstance] @hasInverse(field: iamRole)
2425
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Config } from 'aws-sdk/lib/config'
2+
import SSM, { DescribeDocumentPermissionResponse } from 'aws-sdk/clients/ssm'
3+
import STS, { GetCallerIdentityResponse } from 'aws-sdk/clients/sts'
4+
import { PromiseResult } from 'aws-sdk/lib/request'
5+
import { AWSError } from 'aws-sdk/lib/error'
6+
import isEmpty from 'lodash/isEmpty'
7+
import groupBy from 'lodash/groupBy'
8+
import { convertAwsTagsToTagMap } from '../../utils/format'
9+
import { TagMap } from '../../types'
10+
import { initTestEndpoint } from '../../utils'
11+
import AwsErrorLog from '../../utils/errorLog'
12+
13+
const serviceName = 'systemsManagerDocument'
14+
const endpoint = initTestEndpoint(serviceName)
15+
const errorLog = new AwsErrorLog(serviceName)
16+
export interface RawAwsSystemsManagerDocument
17+
extends Omit<SSM.DocumentIdentifier, 'Tags'> {
18+
region: string
19+
accountId: string
20+
permissions: {
21+
accountIds: string[]
22+
accountSharingInfoList: {
23+
AccountId?: string
24+
SharedDocumentVersion?: string
25+
}[]
26+
}
27+
Tags: TagMap
28+
}
29+
30+
/**
31+
* SystemsManagerDocument
32+
*/
33+
34+
export default async ({
35+
regions,
36+
config,
37+
}: {
38+
regions: string
39+
config: Config
40+
}): Promise<{ [region: string]: RawAwsSystemsManagerDocument[] }> => {
41+
const result: RawAwsSystemsManagerDocument[] = []
42+
43+
const activeRegions = regions.split(',')
44+
// We need the account in the raw data so we can connect to trags
45+
let account: PromiseResult<GetCallerIdentityResponse, AWSError>
46+
try {
47+
account = await new STS(config).getCallerIdentity().promise()
48+
} catch (err) {
49+
errorLog.generateAwsErrorLog({ functionName: 'getCallerIdentity', err })
50+
}
51+
const accountId = account?.Account
52+
for (const region of activeRegions) {
53+
const client = new SSM({ ...config, region, endpoint })
54+
const systemsManagerDocumentData: SSM.DocumentIdentifier[] = []
55+
try {
56+
const filterParam = { Filters: [{ Key: 'Owner', Values: ['Self'] }] }
57+
const data = await client.listDocuments(filterParam).promise()
58+
systemsManagerDocumentData.push(...data.DocumentIdentifiers)
59+
let marker = data.NextToken
60+
while (marker) {
61+
const nextPage = await client
62+
.listDocuments({ ...filterParam, NextToken: marker })
63+
.promise()
64+
if (!isEmpty(data.DocumentIdentifiers)) {
65+
systemsManagerDocumentData.push(...nextPage.DocumentIdentifiers)
66+
marker = nextPage.NextToken
67+
}
68+
}
69+
} catch (err) {
70+
errorLog.generateAwsErrorLog({ functionName: 'listDocuments', err })
71+
}
72+
for (const doc of systemsManagerDocumentData) {
73+
let documentPermissions: PromiseResult<
74+
DescribeDocumentPermissionResponse,
75+
AWSError
76+
>
77+
try {
78+
documentPermissions = await client
79+
.describeDocumentPermission({
80+
Name: doc.Name,
81+
PermissionType: 'Share',
82+
})
83+
.promise()
84+
} catch (err) {
85+
errorLog.generateAwsErrorLog({
86+
functionName: 'describeDocumentPermission',
87+
err,
88+
})
89+
}
90+
result.push({
91+
...doc,
92+
accountId,
93+
permissions: {
94+
accountIds: documentPermissions?.AccountIds,
95+
accountSharingInfoList: documentPermissions?.AccountSharingInfoList,
96+
},
97+
Tags: convertAwsTagsToTagMap(doc.Tags ?? []),
98+
region,
99+
})
100+
}
101+
}
102+
103+
errorLog.reset()
104+
return groupBy(result, 'region')
105+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import cuid from 'cuid'
2+
import { AwsSystemsManagerDocument } from '../../types/generated'
3+
import { RawAwsSystemsManagerDocument } from './data'
4+
import { formatTagsFromMap } from '../../utils/format'
5+
import { ssmDocumentArn } from '../../utils/generateArns'
6+
7+
/**
8+
* SystemsManagerDocument
9+
*/
10+
11+
export default ({
12+
account,
13+
service: rawData,
14+
region,
15+
}: {
16+
account: string
17+
service: RawAwsSystemsManagerDocument
18+
region: string
19+
}): AwsSystemsManagerDocument => {
20+
const {
21+
Name: name,
22+
CreatedDate: createdDate,
23+
Owner: owner,
24+
PlatformTypes: platformTypes,
25+
DocumentVersion: documentVersion,
26+
DocumentType: documentType,
27+
SchemaVersion: schemaVersion,
28+
DocumentFormat: documentFormat,
29+
TargetType: targetType,
30+
Tags: tags,
31+
permissions
32+
} = rawData
33+
34+
const formattedPermissions = {
35+
accountIds: permissions?.accountIds,
36+
accountSharingInfoList: permissions?.accountSharingInfoList?.map(({ AccountId, SharedDocumentVersion }) => ({
37+
id: cuid(),
38+
accountId: AccountId,
39+
sharedDocumentVersion: SharedDocumentVersion
40+
}))
41+
}
42+
const arn = ssmDocumentArn({ region, account, name })
43+
return {
44+
id: arn,
45+
arn,
46+
region,
47+
accountId: account,
48+
name,
49+
createdDate: createdDate?.toISOString(),
50+
owner,
51+
platformTypes,
52+
documentVersion,
53+
documentType,
54+
schemaVersion,
55+
documentFormat,
56+
targetType,
57+
tags: formatTagsFromMap(tags ?? {}),
58+
permissions: formattedPermissions
59+
}
60+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Service } from '@cloudgraph/sdk'
2+
import BaseService from '../base'
3+
import format from './format'
4+
import getData from './data'
5+
import mutation from './mutation'
6+
7+
export default class SystemsManagerDocument extends BaseService implements Service {
8+
format = format.bind(this)
9+
10+
getData = getData.bind(this)
11+
12+
mutation = mutation
13+
}
14+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default `mutation($input: [AddawsSystemsManagerDocumentInput!]!) {
2+
addawsSystemsManagerDocument(input: $input, upsert: true) {
3+
numUids
4+
}
5+
}`;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
type awsSystemsManagerDocument @key(fields: "arn") {
2+
id: String! @id @search(by: [hash])
3+
arn: String! @id @search(by: [hash, regexp])
4+
accountId: String! @search(by: [hash])
5+
region: String @search(by: [hash])
6+
name: String @search(by: [hash, regexp])
7+
createdDate: DateTime @search(by: [day])
8+
owner: String @search(by: [hash, regexp])
9+
platformTypes: [String]
10+
documentVersion: String @search(by: [hash, regexp])
11+
documentType: String @search(by: [hash, regexp])
12+
schemaVersion: String @search(by: [hash, regexp])
13+
documentFormat: String @search(by: [hash, regexp])
14+
targetType: String @search(by: [hash, regexp])
15+
tags: [awsRawTag]
16+
permissions: awsSystemsManagerDocumentPermissions
17+
}
18+
19+
type awsSystemsManagerDocumentPermissions {
20+
accountIds: [String] @search(by: [hash, regexp])
21+
accountSharingInfoList: [awsSystemsManagerDocumentPermissionsSharingList]
22+
}
23+
24+
type awsSystemsManagerDocumentPermissionsSharingList {
25+
id: String! @id @search(by: [hash])
26+
accountId: String @search(by: [hash, regexp])
27+
sharedDocumentVersion: String @search(by: [hash, regexp])
28+
}

0 commit comments

Comments
 (0)