From 22c68fe13e1a4a849fcb903cfac6491fec796083 Mon Sep 17 00:00:00 2001 From: Jussi Hallila Date: Wed, 12 Jun 2024 00:11:11 +0200 Subject: [PATCH] Make AWS providers use official AWS provided integration config to handle AWS credentials. --- .changeset/healthy-balloons-swim.md | 6 +++ packages/backend/src/plugins/catalog.ts | 2 +- .../catalog-backend-module-aws/package.json | 7 ++- .../src/providers/AWSDynamoDbTableProvider.ts | 8 ++-- .../src/providers/AWSEC2Provider.ts | 8 ++-- .../src/providers/AWSEKSClusterProvider.ts | 8 ++-- .../src/providers/AWSEntityProvider.ts | 43 +++++++++++-------- .../src/providers/AWSIAMRoleProvider.ts | 8 ++-- .../src/providers/AWSIAMUserProvider.ts | 8 ++-- .../providers/AWSLambdaFunctionProvider.ts | 8 ++-- .../AWSOrganizationAccountsProvider.ts | 11 ++--- .../src/providers/AWSRDSProvider.ts | 8 ++-- .../src/providers/AWSS3BucketProvider.ts | 8 ++-- .../catalog-backend-module-aws/src/types.ts | 4 +- 14 files changed, 72 insertions(+), 65 deletions(-) create mode 100644 .changeset/healthy-balloons-swim.md diff --git a/.changeset/healthy-balloons-swim.md b/.changeset/healthy-balloons-swim.md new file mode 100644 index 000000000..1ca56e26c --- /dev/null +++ b/.changeset/healthy-balloons-swim.md @@ -0,0 +1,6 @@ +--- +'@roadiehq/catalog-backend-module-aws': major +--- + +Modify configuration shapes to conform to AWS provided Backstage integration configuration. Start using the official credentials provider for AWS resources. +BREAKING: Config shape has been modified to match official integration config. Change `roleArn` to `roleName`. diff --git a/packages/backend/src/plugins/catalog.ts b/packages/backend/src/plugins/catalog.ts index c50d465f8..7eea658d2 100644 --- a/packages/backend/src/plugins/catalog.ts +++ b/packages/backend/src/plugins/catalog.ts @@ -54,7 +54,7 @@ export default async function createPlugin( builder.addEntityProvider(orgProvider); providers.push(orgProvider); - for (const config of env.config.getOptionalConfigArray('integrations.aws') || + for (const config of env.config.getOptionalConfigArray('aws.accounts') || []) { const s3Provider = AWSS3BucketProvider.fromConfig(config, env); const lambdaProvider = AWSLambdaFunctionProvider.fromConfig(config, env); diff --git a/plugins/backend/catalog-backend-module-aws/package.json b/plugins/backend/catalog-backend-module-aws/package.json index 43c01f2ee..4cdf1bad9 100644 --- a/plugins/backend/catalog-backend-module-aws/package.json +++ b/plugins/backend/catalog-backend-module-aws/package.json @@ -45,6 +45,7 @@ "@aws-sdk/client-sts": "^3.76.0", "@aws-sdk/credential-providers": "^3.76.0", "@aws-sdk/lib-dynamodb": "^3.76.0", + "@backstage/integration-aws-node": "^0.1.12", "@backstage/backend-common": "^0.21.7", "@backstage/catalog-client": "^1.6.4", "@backstage/catalog-model": "^1.4.5", @@ -68,8 +69,6 @@ "yaml": "^2.2.2" }, "files": [ - "dist", - "config.d.ts" - ], - "configSchema": "config.d.ts" + "dist" + ] } diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSDynamoDbTableProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSDynamoDbTableProvider.ts index fa087712c..5674d3aa2 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSDynamoDbTableProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSDynamoDbTableProvider.ts @@ -38,12 +38,12 @@ export class AWSDynamoDbTableProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSDynamoDbTableProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -58,8 +58,8 @@ export class AWSDynamoDbTableProvider extends AWSEntityProvider { } const groups = await this.getGroups(); - const credentials = this.getCredentials(); - const ddb = new DynamoDB({ credentials }); + const credentials = this.getCredentialsProvider(); + const ddb = new DynamoDB(credentials); const defaultAnnotations = await this.buildDefaultAnnotations(); this.logger.info( diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEC2Provider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEC2Provider.ts index 45f9107bf..8eb616f24 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEC2Provider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEC2Provider.ts @@ -38,12 +38,12 @@ export class AWSEC2Provider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSEC2Provider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -61,8 +61,8 @@ export class AWSEC2Provider extends AWSEntityProvider { this.logger.info(`Providing ec2 resources from aws: ${this.accountId}`); const ec2Resources: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const ec2 = new EC2({ credentials, region: this.region }); + const credentials = this.getCredentialsProvider(); + const ec2 = new EC2(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEKSClusterProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEKSClusterProvider.ts index 7fb4f3501..7726790a1 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEKSClusterProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEKSClusterProvider.ts @@ -41,12 +41,12 @@ export class AWSEKSClusterProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSEKSClusterProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -66,8 +66,8 @@ export class AWSEKSClusterProvider extends AWSEntityProvider { ); const eksResources: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const eks = new EKS({ credentials, region: this.region }); + const credentials = this.getCredentialsProvider(); + const eks = new EKS(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEntityProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEntityProvider.ts index ed477568d..beb78b5b7 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSEntityProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSEntityProvider.ts @@ -20,7 +20,6 @@ import { } from '@backstage/plugin-catalog-node'; import * as winston from 'winston'; import { AccountConfig } from '../types'; -import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; import { STS } from '@aws-sdk/client-sts'; import { ANNOTATION_ORIGIN_LOCATION, @@ -28,18 +27,17 @@ import { } from '@backstage/catalog-model'; import { ANNOTATION_ACCOUNT_ID } from '../annotations'; import { CatalogApi } from '@backstage/catalog-client'; -import { parse as parseArn } from '@aws-sdk/util-arn-parser'; +import { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node'; +import { ConfigReader } from '@backstage/config'; export abstract class AWSEntityProvider implements EntityProvider { - protected readonly accountId: string; - protected readonly roleArn: string; - private readonly externalId?: string; - protected readonly region: string; protected readonly providerId?: string; protected readonly logger: winston.Logger; protected connection?: EntityProviderConnection; private readonly ownerTag: string | undefined; protected readonly catalogApi?: CatalogApi; + private credentialsManager: DefaultAwsCredentialsManager; + private account: AccountConfig; public abstract getProviderName(): string; @@ -52,26 +50,29 @@ export abstract class AWSEntityProvider implements EntityProvider { ownerTag?: string; }, ) { - this.accountId = account.accountId; - this.roleArn = account.roleArn; - this.externalId = account.externalId; - this.region = account.region; this.logger = options.logger; this.providerId = options.providerId; this.ownerTag = options.ownerTag; this.catalogApi = options.catalogApi; + this.account = account; + this.credentialsManager = DefaultAwsCredentialsManager.fromConfig( + new ConfigReader(account), + ); + } + + get accountId() { + return this.account.accountId; + } + get region() { + return this.account.region; } protected getOwnerTag() { return this.ownerTag ?? 'owner'; } - protected getCredentials() { - const region = parseArn(this.roleArn).region; - return fromTemporaryCredentials({ - params: { RoleArn: this.roleArn, ExternalId: this.externalId }, - clientConfig: { region: region }, - }); + protected async getCredentialsProvider() { + return await this.credentialsManager.getCredentialProvider(); } protected async getGroups() { @@ -95,13 +96,17 @@ export abstract class AWSEntityProvider implements EntityProvider { } protected async buildDefaultAnnotations() { - const sts = new STS({ credentials: this.getCredentials() }); + const sts = new STS(await this.getCredentialsProvider()); const account = await sts.getCallerIdentity({}); const defaultAnnotations: { [name: string]: string } = { - [ANNOTATION_LOCATION]: `${this.getProviderName()}:${this.roleArn}`, - [ANNOTATION_ORIGIN_LOCATION]: `${this.getProviderName()}:${this.roleArn}`, + [ANNOTATION_LOCATION]: `${this.getProviderName()}:${ + this.account.roleName + }`, + [ANNOTATION_ORIGIN_LOCATION]: `${this.getProviderName()}:${ + this.account.roleName + }`, }; if (account.Account) { diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMRoleProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMRoleProvider.ts index 8717ae261..f4b6641c5 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMRoleProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMRoleProvider.ts @@ -39,12 +39,12 @@ export class AWSIAMRoleProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSIAMRoleProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -64,11 +64,11 @@ export class AWSIAMRoleProvider extends AWSEntityProvider { ); const roleResources: ResourceEntity[] = []; - const credentials = this.getCredentials(); + const credentials = this.getCredentialsProvider(); const defaultAnnotations = this.buildDefaultAnnotations(); - const iam = new IAM({ credentials, region: this.region }); + const iam = new IAM(credentials); const paginatorConfig = { client: iam, diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMUserProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMUserProvider.ts index cdf694e20..99b527a7a 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMUserProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSIAMUserProvider.ts @@ -39,12 +39,12 @@ export class AWSIAMUserProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSIAMUserProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -63,11 +63,11 @@ export class AWSIAMUserProvider extends AWSEntityProvider { ); const userResources: UserEntity[] = []; - const credentials = this.getCredentials(); + const credentials = this.getCredentialsProvider(); const defaultAnnotations = this.buildDefaultAnnotations(); - const iam = new IAM({ credentials, region: this.region }); + const iam = new IAM(credentials); const paginatorConfig = { client: iam, diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSLambdaFunctionProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSLambdaFunctionProvider.ts index 3bec0c2fb..f6c407ed8 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSLambdaFunctionProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSLambdaFunctionProvider.ts @@ -42,12 +42,12 @@ export class AWSLambdaFunctionProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSLambdaFunctionProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -68,8 +68,8 @@ export class AWSLambdaFunctionProvider extends AWSEntityProvider { const lambdaComponents: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const lambda = new Lambda({ credentials, region: this.region }); + const credentials = this.getCredentialsProvider(); + const lambda = new Lambda(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSOrganizationAccountsProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSOrganizationAccountsProvider.ts index 0840b6725..4797ec7e4 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSOrganizationAccountsProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSOrganizationAccountsProvider.ts @@ -47,12 +47,12 @@ export class AWSOrganizationAccountsProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSOrganizationAccountsProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -74,11 +74,8 @@ export class AWSOrganizationAccountsProvider extends AWSEntityProvider { ); const accountResources: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const organizationsClient = new OrganizationsClient({ - credentials, - region: this.region, - }); + const credentials = this.getCredentialsProvider(); + const organizationsClient = new OrganizationsClient(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSRDSProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSRDSProvider.ts index cc2c7a410..4bf676c37 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSRDSProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSRDSProvider.ts @@ -37,12 +37,12 @@ export class AWSRDSProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSRDSProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -60,8 +60,8 @@ export class AWSRDSProvider extends AWSEntityProvider { this.logger.info(`Providing RDS resources from aws: ${this.accountId}`); const rdsResources: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const rdsClient = new RDS({ credentials, region: this.region }); + const credentials = this.getCredentialsProvider(); + const rdsClient = new RDS(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/providers/AWSS3BucketProvider.ts b/plugins/backend/catalog-backend-module-aws/src/providers/AWSS3BucketProvider.ts index 8642aaa54..afa6d92ac 100644 --- a/plugins/backend/catalog-backend-module-aws/src/providers/AWSS3BucketProvider.ts +++ b/plugins/backend/catalog-backend-module-aws/src/providers/AWSS3BucketProvider.ts @@ -39,12 +39,12 @@ export class AWSS3BucketProvider extends AWSEntityProvider { }, ) { const accountId = config.getString('accountId'); - const roleArn = config.getString('roleArn'); + const roleName = config.getString('roleName'); const externalId = config.getOptionalString('externalId'); const region = config.getString('region'); return new AWSS3BucketProvider( - { accountId, roleArn, externalId, region }, + { accountId, roleName, externalId, region }, options, ); } @@ -64,8 +64,8 @@ export class AWSS3BucketProvider extends AWSEntityProvider { ); const s3Resources: ResourceEntity[] = []; - const credentials = this.getCredentials(); - const s3 = new S3({ credentials, region: this.region }); + const credentials = this.getCredentialsProvider(); + const s3 = new S3(credentials); const defaultAnnotations = this.buildDefaultAnnotations(); diff --git a/plugins/backend/catalog-backend-module-aws/src/types.ts b/plugins/backend/catalog-backend-module-aws/src/types.ts index 0a99e5592..64c6a7096 100644 --- a/plugins/backend/catalog-backend-module-aws/src/types.ts +++ b/plugins/backend/catalog-backend-module-aws/src/types.ts @@ -59,7 +59,7 @@ export interface AWSAccountProviderConfig { /** * Role to assume for this account ID */ - roleArn: string; + roleName: string; /** * Region to use for this account ID */ @@ -77,7 +77,7 @@ export interface AWSAccountProviderConfig { export type AccountConfig = { accountId: string; - roleArn: string; + roleName: string; externalId?: string; region: string; };