From 3c4eb7eab218ca16af3dc76159d20859cc7c0e47 Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Tue, 16 Jun 2026 16:16:29 -0700 Subject: [PATCH 1/6] APIGOV-32905 CLI - update install agents comment flow for Amazon Bedrock Discovery agent flow --- src/lib/engage/types.ts | 25 +++++ .../engage/utils/agents/flows/awsAgents.ts | 68 +++++++++++- .../utils/agents/flows/awsSaasAgents.ts | 92 +++++++++++++++- .../utils/agents/templates/awsTemplates.ts | 19 ++++ src/lib/engage/utils/utils.ts | 2 + .../on-prem/test-aws.onprem.js | 103 +++++++++++++++++- .../install-service/saas/test-aws.saas.js | 39 ++++++- 7 files changed, 338 insertions(+), 10 deletions(-) diff --git a/src/lib/engage/types.ts b/src/lib/engage/types.ts index 7d1b888d..af859fd7 100644 --- a/src/lib/engage/types.ts +++ b/src/lib/engage/types.ts @@ -752,6 +752,31 @@ export enum GatewayMode { GatewayOnlyMode = 'Gateway only', } +export enum AWSGatewayMode { + APIGateway = 'api-gateway', + AgentCoreGateway = 'agent-core-gateway', +} + +export class AWSAgentCoreConfig { + logGroupPrefix?: string; + iamAuthEnabled?: boolean; + + constructor(logGroupPrefix?: string, iamAuthEnabled?: boolean) { + this.logGroupPrefix = logGroupPrefix ?? ''; + this.iamAuthEnabled = iamAuthEnabled ?? false; + } +} + +export class AWSCognitoConfig { + userPoolId: string; + region?: string; + + constructor(userPoolId: string, region?: string) { + this.userPoolId = userPoolId; + this.region = region; + } +} + export enum AgentNames { AKAMAI_CA = 'akamai-compliance-agent', AWS_DA = 'aws-apigw-discovery-agent', diff --git a/src/lib/engage/utils/agents/flows/awsAgents.ts b/src/lib/engage/utils/agents/flows/awsAgents.ts index e97ea2fa..0017cfa3 100644 --- a/src/lib/engage/utils/agents/flows/awsAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsAgents.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import fs from 'fs'; import logger from '../../../../logger.js'; import { dataService } from '../../../../request.js'; -import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSRegions, BasePaths, BundleType, GatewayTypes, InstallationFlowMethods, PublicDockerRepoBaseUrl, PublicRepoUrl, TrueFalse, YesNo, YesNoChoices } from '../../../types.js'; +import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSCognitoConfig, AWSRegions, BasePaths, BundleType, GatewayTypes, InstallationFlowMethods, PublicDockerRepoBaseUrl, PublicRepoUrl, TrueFalse, YesNo, YesNoChoices } from '../../../types.js'; import { askInput, askList, validateInputLength, validateRegex } from '../../basic-prompts.js'; import { isWindows, writeTemplates, writeToFile } from '../../utils.js'; import { AWSAgentValues } from '../index.js'; @@ -77,6 +77,14 @@ export const AWSPrompts = { FULL_TRANSACTION_LOGGING: 'Do you want to enable Full Transaction Logging? Please note that CloudWatch costs would increase when Full Transaction Logging is enabled', TA_QUEUE: 'Enter the traceability queue name', VPC_ID: 'Enter the VPC ID to deploy the EC2 instance to. Leave blank to create entire infrastructure', + AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', + AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', + AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', + ENTER_MORE_COGNITO_USER_POOLS: 'Do you want to enter another Cognito User Pool for Agent Core Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pools used for authentication in Agent Core Gateway mode', + COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', + ASK_COGNITO_REGION: 'Do you want to specify a region for the Cognito User Pool? (If not, the agent will use the same region as the gateway)', + COGNITO_REGION: 'Select the AWS region of the Cognito user pool. Defaults to the agent region if omitted', }; export const askBundleType = async (): Promise => { @@ -310,6 +318,64 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr awsAgentValues.fullTransactionLogging = fullTransactionLogging; + awsAgentValues.agentCoreGatewayMode = (await askList({ + msg: AWSPrompts.AGENT_CORE_GATEWAY_MODE, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + if (awsAgentValues.agentCoreGatewayMode) { + awsAgentValues.agentCore.logGroupPrefix = (await askInput({ + msg: AWSPrompts.AGENT_CORE_LOG_GROUP_PREFIX, + defaultValue: awsAgentValues.agentCore.logGroupPrefix !== '' ? awsAgentValues.agentCore.logGroupPrefix : undefined, + allowEmptyInput: true, + })) as string; + + awsAgentValues.agentCore.iamAuthEnabled = (await askList({ + msg: AWSPrompts.AGENT_CORE_IAM_AUTH, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + installConfig.log(chalk.gray(AWSPrompts.COGNITO)); + const cognitoUserPools: AWSCognitoConfig[] = []; + let askCognitoUserPools = true; + + while (askCognitoUserPools) { + const userPoolId = (await askInput({ + msg: AWSPrompts.COGNITO_USER_POOL_ID, + })) as string; + + const askRegion = (await askList({ + msg: AWSPrompts.ASK_COGNITO_REGION, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + if (askRegion) { + + const regions = Object.values(AWSRegions).map((str) => ({ name: str, value: str })); + + const region = await askList({ + msg: AWSPrompts.COGNITO_REGION, + choices: regions, + + }); + + cognitoUserPools.push({ userPoolId, region }); + } else { + cognitoUserPools.push({ userPoolId, region: awsAgentValues.region }); + } + + askCognitoUserPools = await askList({ + msg: AWSPrompts.ENTER_MORE_COGNITO_USER_POOLS, + choices: YesNoChoices, + default: YesNo.No, + }) === YesNo.Yes; + } + + awsAgentValues.cognito = cognitoUserPools; + } + // set agent versions awsAgentValues.cloudFormationConfig.DiscoveryAgentVersion = installConfig.daVersion; awsAgentValues.cloudFormationConfig.TraceabilityAgentVersion = installConfig.taVersion; diff --git a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts index f8997e82..74c9fb79 100644 --- a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import logger from '../../../../logger.js'; import { ApiServerClient } from '../../../clients-external/apiserverclient.js'; import { DefinitionsManager } from '../../../results/DefinitionsManager.js'; -import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, BundleType, GatewayTypes, InstallationFlowMethods, SaaSGatewayTypes, YesNo, YesNoChoices } from '../../../types.js'; +import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSAgentCoreConfig, AWSCognitoConfig, AWSGatewayMode, AWSRegions, BundleType, GatewayTypes, InstallationFlowMethods, SaaSGatewayTypes, YesNo, YesNoChoices } from '../../../types.js'; import { askInput, askList, validateInputLength, validateRegex } from '../../basic-prompts.js'; import * as helpers from '../index.js'; import { @@ -23,12 +23,17 @@ class AWSDataplaneConfig extends DataplaneConfig { accessLogARN: string; fullTransactionLogging: boolean; stageTagName: string; + gatewayMode: AWSGatewayMode; + agentCore: AWSAgentCoreConfig; + cognito: AWSCognitoConfig[]; - constructor(arn: string, enableFullTransactionLogging: boolean, stageTagName: string) { + constructor(arn: string, enableFullTransactionLogging: boolean, stageTagName: string, agentCoreConfig: AWSAgentCoreConfig, cognitoConfig: AWSCognitoConfig[]) { super('AWS'); this.accessLogARN = arn; this.fullTransactionLogging = enableFullTransactionLogging; this.stageTagName = stageTagName; + this.agentCore = agentCoreConfig; + this.cognito = cognitoConfig; } } @@ -47,6 +52,9 @@ class SaasAWSAgentValues extends SaasAgentValues { accessLogARN: string; fullTransactionLogging: boolean; stageTagName: string; + agentCoreGatewayMode: boolean; + agentCore: AWSAgentCoreConfig; + cognito: AWSCognitoConfig[]; constructor() { super(); @@ -59,8 +67,9 @@ class SaasAWSAgentValues extends SaasAgentValues { this.accessLogARN = ''; this.fullTransactionLogging = false; this.stageTagName = ''; + this.agentCore = {} as AWSAgentCoreConfig; + this.cognito = [] as AWSCognitoConfig[]; } - override getAccessData(): string { if (this.authType === AWSAuthType.KEYS) { return JSON.stringify({ @@ -87,6 +96,14 @@ const SaasPrompts = { ACCESS_LOG_ARN: 'Enter the ARN for the Access Log that the Discovery will add and the Traceability will use', STAGE_TAG_NAME: 'Enter the name of the tag on AWS API Gateway Stage that holds mapped stage on Amplify Engage', FULL_TRANSACTION_LOGGING: 'Do you want to enable Full Transaction Logging? Please note that CloudWatch costs would increase when Full Transaction Logging is enabled', + AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', + AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', + AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', + ENTER_MORE_COGNITO_USER_POOLS: 'Do you want to enter another Cognito User Pool for Agent Core Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pools used for authentication in Agent Core Gateway mode', + COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', + ASK_COGNITO_REGION: 'Do you want to specify a region for the Cognito User Pool? (If not, the agent will use the same region as the gateway)', + COGNITO_REGION: 'Select the AWS region of the Cognito user pool. Defaults to the agent region if omitted', }; export const askBundleType = async (): Promise => { @@ -155,6 +172,69 @@ const askForAWSCredentials = async (agentValues: SaasAWSAgentValues, log: (text: return agentValues; }; +const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: (text: string) => void = () => {}): Promise => { + agentValues.agentCoreGatewayMode = (await askList({ + msg: SaasPrompts.AGENT_CORE_GATEWAY_MODE, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + if (agentValues.agentCoreGatewayMode) { + agentValues.agentCore.logGroupPrefix = (await askInput({ + msg: SaasPrompts.AGENT_CORE_LOG_GROUP_PREFIX, + defaultValue: agentValues.agentCore.logGroupPrefix !== '' ? agentValues.agentCore.logGroupPrefix : undefined, + allowEmptyInput: true, + })) as string; + + agentValues.agentCore.iamAuthEnabled = (await askList({ + msg: SaasPrompts.AGENT_CORE_IAM_AUTH, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + log(chalk.gray(SaasPrompts.COGNITO)); + const cognitoUserPools: AWSCognitoConfig[] = []; + let askCognitoUserPools = true; + + while (askCognitoUserPools) { + const userPoolId = (await askInput({ + msg: SaasPrompts.COGNITO_USER_POOL_ID, + })) as string; + + const askRegion = (await askList({ + msg: SaasPrompts.ASK_COGNITO_REGION, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + if (askRegion) { + + const regions = Object.values(AWSRegions).map((str) => ({ name: str, value: str })); + + const region = await askList({ + msg: SaasPrompts.COGNITO_REGION, + choices: regions, + + }); + + cognitoUserPools.push({ userPoolId, region }); + } else { + cognitoUserPools.push({ userPoolId, region: agentValues.region }); + } + + askCognitoUserPools = await askList({ + msg: SaasPrompts.ENTER_MORE_COGNITO_USER_POOLS, + choices: YesNoChoices, + default: YesNo.No, + }) === YesNo.Yes; + } + + agentValues.cognito = cognitoUserPools; + + } + + return agentValues; +}; + export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Promise => { installConfig.log('\nCONNECTION TO AMAZON API GATEWAY:'); installConfig.log( @@ -174,6 +254,8 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr validate: validateInputLength(STAGE_TAG_NAME_LENGTH, 'Maximum length of \'stage tag name\' is 127'), })) as string; + agentValues = await askForAgentCoreGatewayMode(awsValues, installConfig.log); + if (installConfig.switches.isTaEnabled) { installConfig.log(chalk.gray('\nThe access log ARN is a cloud watch log group amazon resource name')); awsValues.accessLogARN = (await askInput({ @@ -226,7 +308,9 @@ export const completeInstall = async ( dataplaneConfig = new AWSDataplaneConfig( awsAgentValues.accessLogARN, awsAgentValues.fullTransactionLogging, - awsAgentValues.stageTagName + awsAgentValues.stageTagName, + awsAgentValues.agentCore, + awsAgentValues.cognito ); } else { dataplaneConfig = new DataplaneConfig('AWS'); diff --git a/src/lib/engage/utils/agents/templates/awsTemplates.ts b/src/lib/engage/utils/agents/templates/awsTemplates.ts index 64d48608..8fe52123 100644 --- a/src/lib/engage/utils/agents/templates/awsTemplates.ts +++ b/src/lib/engage/utils/agents/templates/awsTemplates.ts @@ -1,4 +1,6 @@ import { + AWSAgentCoreConfig, + AWSCognitoConfig, CentralAgentConfig, CloudFormationConfig, TraceabilityConfig, @@ -19,6 +21,9 @@ export class AWSAgentValues { centralConfig: CentralAgentConfig; traceabilityConfig: TraceabilityConfig; cloudFormationConfig: CloudFormationConfig; + agentCoreGatewayMode: boolean; + agentCore: AWSAgentCoreConfig; + cognito: AWSCognitoConfig[]; constructor(awsDeployment: string) { this.accessKey = awsDeployment === 'Other' ? '**Insert Access Key**' : ''; @@ -31,6 +36,9 @@ export class AWSAgentValues { this.centralConfig = new CentralAgentConfig(); this.traceabilityConfig = new TraceabilityConfig(); this.cloudFormationConfig = new CloudFormationConfig(); + this.agentCoreGatewayMode = false; + this.agentCore = new AWSAgentCoreConfig(); + this.cognito = []; } updateCloudFormationConfig = () => { @@ -102,6 +110,17 @@ AWS_AUTH_SECRETKEY={{secretKey}} {{/if}} AWS_LOGGROUP={{logGroup}} AWS_STAGETAGNAME={{stageTagName}} +{{#if agentCoreGatewayMode}} +AWS_GATEWAYMODE=agentcore-gateway +AWS_AGENTCORE_LOGGROUPPREFIX={{agentCore.logGroupPrefix}} +AWS_AGENTCORE_IAMAUTHENABLED={{agentCore.iamAuthEnabled}} +{{#each cognito}} +AWS_COGNITO_USERPOOLID_{{add @index 1}}={{this.userPoolId}} +AWS_COGNITO_REGION_{{add @index 1}}={{this.region}} +{{/each}} +{{else}} +AWS_GATEWAYMODE=api-gateway +{{/if}} # Amplify Central configs CENTRAL_AGENTNAME={{centralConfig.daAgentName}} diff --git a/src/lib/engage/utils/utils.ts b/src/lib/engage/utils/utils.ts index 6acc5373..b11b5373 100644 --- a/src/lib/engage/utils/utils.ts +++ b/src/lib/engage/utils/utils.ts @@ -549,6 +549,8 @@ export const writeTemplates = (fileName: string, values: object, templateFunc: ( writeToFile(fileName, data); }; +hbs.registerHelper('add', (a: number, b: number) => a + b); + export const buildTemplate = (templateFunc: () => string, input: object): string => { const template = hbs.compile(templateFunc(), { noEscape: true }); return template(input); diff --git a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js index 31572925..a0533fe1 100644 --- a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js +++ b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js @@ -94,6 +94,7 @@ describe('AWS on-prem agent flow', () => { flowModule.DeploymentTypes.EC2, 'Yes', 'No', + 'No', 't3.micro', 'Yes', ]; @@ -124,8 +125,9 @@ describe('AWS on-prem agent flow', () => { expect(result.logGroup).to.equal('/aws/apigw/logs'); expect(result.stageTagName).to.equal('stage-tag'); expect(result.fullTransactionLogging).to.equal(false); + expect(result.agentCoreGatewayMode).to.equal(false); expect(td.explain(promptStubs.askInput).callCount).to.equal(12); - expect(td.explain(promptStubs.askList).callCount).to.equal(5); + expect(td.explain(promptStubs.askList).callCount).to.equal(6); }); it('skips VPC-derived prompts when EC2 VPC is empty', async () => { @@ -133,6 +135,7 @@ describe('AWS on-prem agent flow', () => { flowModule.DeploymentTypes.EC2, 'Yes', 'No', + 'No', 't3.micro', ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -157,7 +160,7 @@ describe('AWS on-prem agent flow', () => { expect(result.cloudFormationConfig.SecurityGroup).to.equal(''); expect(result.cloudFormationConfig.Subnet).to.equal(''); expect(td.explain(promptStubs.askInput).callCount).to.equal(10); - expect(td.explain(promptStubs.askList).callCount).to.equal(4); + expect(td.explain(promptStubs.askList).callCount).to.equal(5); }); it('collects ECS-only deployment prompts', async () => { @@ -165,6 +168,7 @@ describe('AWS on-prem agent flow', () => { flowModule.DeploymentTypes.ECS_FARGATE, 'Yes', 'No', + 'No', ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -190,7 +194,7 @@ describe('AWS on-prem agent flow', () => { expect(result.cloudFormationConfig.EC2KeyName).to.equal(''); expect(logs.some((line) => line.includes('ECS Cluster Name'))).to.equal(true); expect(td.explain(promptStubs.askInput).callCount).to.equal(10); - expect(td.explain(promptStubs.askList).callCount).to.equal(3); + expect(td.explain(promptStubs.askList).callCount).to.equal(4); }); it('collects minimal prompts for OTHER deployment type', async () => { @@ -198,6 +202,7 @@ describe('AWS on-prem agent flow', () => { flowModule.DeploymentTypes.OTHER, 'Yes', 'No', + 'No', ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -218,7 +223,82 @@ describe('AWS on-prem agent flow', () => { expect(result.cloudFormationConfig.ECSClusterName).to.equal(''); expect(logs.some((line) => line.includes('AWS Access Key'))).to.equal(true); expect(td.explain(promptStubs.askInput).callCount).to.equal(5); - expect(td.explain(promptStubs.askList).callCount).to.equal(3); + expect(td.explain(promptStubs.askList).callCount).to.equal(4); + }); + + it('enables agent core gateway mode and collects a single cognito pool', async () => { + const askListResponses = [ + flowModule.DeploymentTypes.OTHER, + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging + 'Yes', // AGENT_CORE_GATEWAY_MODE + 'Yes', // iamAuthEnabled + 'No', // askRegion? (use agent region) + 'No', // enterMore? + ]; + td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); + + const askInputResponses = [ + 'agents-bucket', + '/aws/apigw/logs', + 'stage-tag', + '/aws/prefix', + 'us-east-1_123456789', + '/aws/da/logs', + '/aws/ta/logs', + ]; + td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); + + const result = await flowModule.gatewayConnectivity(buildInstallConfig({ isDaEnabled: true, isTaEnabled: true })); + + expect(result.agentCoreGatewayMode).to.equal(true); + expect(result.agentCore.logGroupPrefix).to.equal('/aws/prefix'); + expect(result.agentCore.iamAuthEnabled).to.equal(true); + expect(result.cognito).to.have.length(1); + expect(result.cognito[0].userPoolId).to.equal('us-east-1_123456789'); + expect(result.cognito[0].region).to.equal('us-east-1'); + expect(td.explain(promptStubs.askInput).callCount).to.equal(7); + expect(td.explain(promptStubs.askList).callCount).to.equal(7); + }); + + it('enables agent core gateway mode and collects multiple cognito pools', async () => { + const askListResponses = [ + flowModule.DeploymentTypes.OTHER, + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging + 'Yes', // AGENT_CORE_GATEWAY_MODE + 'No', // iamAuthEnabled + 'No', // askRegion? pool 1 + 'Yes', // enterMore? (add another pool) + 'No', // askRegion? pool 2 + 'No', // enterMore? + ]; + td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); + + const askInputResponses = [ + 'agents-bucket', + '/aws/apigw/logs', + 'stage-tag', + '', + 'us-east-1_111111111', + 'eu-west-1_222222222', + '/aws/da/logs', + '/aws/ta/logs', + ]; + td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); + + const result = await flowModule.gatewayConnectivity(buildInstallConfig({ isDaEnabled: true, isTaEnabled: true })); + + expect(result.agentCoreGatewayMode).to.equal(true); + expect(result.agentCore.logGroupPrefix).to.equal(''); + expect(result.agentCore.iamAuthEnabled).to.equal(false); + expect(result.cognito).to.have.length(2); + expect(result.cognito[0].userPoolId).to.equal('us-east-1_111111111'); + expect(result.cognito[0].region).to.equal('us-east-1'); + expect(result.cognito[1].userPoolId).to.equal('eu-west-1_222222222'); + expect(result.cognito[1].region).to.equal('us-east-1'); + expect(td.explain(promptStubs.askInput).callCount).to.equal(8); + expect(td.explain(promptStubs.askList).callCount).to.equal(9); }); it('stops question flow when AWS region lookup fails', async () => { @@ -354,6 +434,9 @@ function createHelpersStubs() { this.logGroup = ''; this.region = 'us-east-1'; this.stageTagName = ''; + this.agentCoreGatewayMode = false; + this.agentCore = { logGroupPrefix: '', iamAuthEnabled: false }; + this.cognito = []; this.cloudFormationConfig = { APIGWCWRoleSetup: '', APIGWTrafficLogGroupName: '/aws/apigw/logs', @@ -383,6 +466,18 @@ function createHelpersStubs() { return { AWSAgentValues, + AWSCognitoConfig: class AWSCognitoConfig { + constructor(userPoolId, region) { + this.userPoolId = userPoolId; + this.region = region; + } + }, + AWSAgentCoreConfig: class AWSAgentCoreConfig { + constructor(logGroupPrefix, iamAuthEnabled) { + this.logGroupPrefix = logGroupPrefix ?? ''; + this.iamAuthEnabled = iamAuthEnabled ?? false; + } + }, AWSRegexPatterns: { AWS_REGEXP: /.*/, AWS_REGEXP_LOG_GROUP_NAME: /.*/, diff --git a/test/lib/engage/services/install-service/saas/test-aws.saas.js b/test/lib/engage/services/install-service/saas/test-aws.saas.js index 677bcfb9..de00d2ac 100644 --- a/test/lib/engage/services/install-service/saas/test-aws.saas.js +++ b/test/lib/engage/services/install-service/saas/test-aws.saas.js @@ -70,12 +70,13 @@ describe('AWS SaaS agent flow', () => { 'arn:aws:logs:us-east-1:000000000000:log-group:my-group', ]; td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); - const askListResponses = [ 'Assume Role Policy', engageTypes.YesNo.Yes ]; + const askListResponses = [ 'Assume Role Policy', engageTypes.YesNo.No, engageTypes.YesNo.Yes ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); const result = await flowModule.AWSSaaSInstallMethods.AskGatewayQuestions(buildInstallConfig(engageTypes.GatewayTypes.AWS_GATEWAY, true)); expect(result.authType).to.equal('Assume Role Policy'); expect(result.assumeRole).to.contain('arn:aws:iam'); + expect(result.agentCoreGatewayMode).to.equal(false); expect(result.fullTransactionLogging).to.equal(true); }); @@ -132,6 +133,7 @@ describe('AWS SaaS agent flow', () => { const askListResponses = [ 'Assume Role Policy', + engageTypes.YesNo.No, engageTypes.YesNo.Yes, engageTypes.YesNo.No, engageTypes.YesNo.No, @@ -154,12 +156,45 @@ describe('AWS SaaS agent flow', () => { expect(result.redaction.maskingCharacter).to.equal('***'); }); + it('collects agent core gateway mode with IAM auth enabled and a single cognito pool', async () => { + const askInputResponses = [ + 'arn:aws:iam::000000000000:role/name-of-role', + 'external-id', + 'stage-tag', + '/aws/prefix', + 'us-east-1_123456789', + 'arn:aws:logs:us-east-1:000000000000:log-group:my-group', + ]; + td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); + + const askListResponses = [ + 'Assume Role Policy', + engageTypes.YesNo.Yes, + engageTypes.YesNo.Yes, + engageTypes.YesNo.No, + engageTypes.YesNo.No, + engageTypes.YesNo.No, + ]; + td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); + + const result = await flowModule.AWSSaaSInstallMethods.AskGatewayQuestions(buildInstallConfig(engageTypes.GatewayTypes.AWS_GATEWAY, true)); + expect(result.agentCoreGatewayMode).to.equal(true); + expect(result.agentCore.logGroupPrefix).to.equal('/aws/prefix'); + expect(result.agentCore.iamAuthEnabled).to.equal(true); + expect(result.cognito).to.have.length(1); + expect(result.cognito[0].userPoolId).to.equal('us-east-1_123456789'); + expect(result.cognito[0].region).to.equal('us-east-1'); + expect(result.fullTransactionLogging).to.equal(false); + }); + it('builds AWS dataplane config when TA enabled', async () => { const installConfig = buildInstallConfig(engageTypes.GatewayTypes.AWS_GATEWAY, true); installConfig.gatewayConfig = { accessLogARN: 'arn:aws:logs:us-east-1:000000000000:log-group:my-group', fullTransactionLogging: true, stageTagName: 'stage-tag', + agentCore: { logGroupPrefix: '/aws/prefix', iamAuthEnabled: true }, + cognito: [ { userPoolId: 'us-east-1_123456789', region: 'us-east-1' } ], redaction: {}, }; @@ -167,6 +202,8 @@ describe('AWS SaaS agent flow', () => { const dataplaneArg = td.explain(saasBaseStubs.createDataplaneResources).calls[0].args[1]; expect(dataplaneArg.type).to.equal('AWS'); expect(dataplaneArg.accessLogARN).to.contain('arn:aws:logs'); + expect(dataplaneArg.agentCore).to.deep.equal({ logGroupPrefix: '/aws/prefix', iamAuthEnabled: true }); + expect(dataplaneArg.cognito).to.deep.equal([ { userPoolId: 'us-east-1_123456789', region: 'us-east-1' } ]); }); it('passes IDP config in completeInstall context', async () => { From 43d5eeec02284ba3227053ef2b10491451b02b30 Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Fri, 26 Jun 2026 15:34:11 -0700 Subject: [PATCH 2/6] APIGOV-32905 Updates to remove region for cognito in AWS agent --- src/lib/engage/types.ts | 10 ---- .../engage/utils/agents/flows/awsAgents.ts | 35 +++----------- .../utils/agents/flows/awsSaasAgents.ts | 47 +++++-------------- .../utils/agents/templates/awsTemplates.ts | 10 ++-- .../on-prem/test-aws.onprem.js | 28 ++++------- .../install-service/saas/test-aws.saas.js | 10 ++-- 6 files changed, 36 insertions(+), 104 deletions(-) diff --git a/src/lib/engage/types.ts b/src/lib/engage/types.ts index af859fd7..9eb168f8 100644 --- a/src/lib/engage/types.ts +++ b/src/lib/engage/types.ts @@ -767,16 +767,6 @@ export class AWSAgentCoreConfig { } } -export class AWSCognitoConfig { - userPoolId: string; - region?: string; - - constructor(userPoolId: string, region?: string) { - this.userPoolId = userPoolId; - this.region = region; - } -} - export enum AgentNames { AKAMAI_CA = 'akamai-compliance-agent', AWS_DA = 'aws-apigw-discovery-agent', diff --git a/src/lib/engage/utils/agents/flows/awsAgents.ts b/src/lib/engage/utils/agents/flows/awsAgents.ts index 0017cfa3..d5d6ecad 100644 --- a/src/lib/engage/utils/agents/flows/awsAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsAgents.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import fs from 'fs'; import logger from '../../../../logger.js'; import { dataService } from '../../../../request.js'; -import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSCognitoConfig, AWSRegions, BasePaths, BundleType, GatewayTypes, InstallationFlowMethods, PublicDockerRepoBaseUrl, PublicRepoUrl, TrueFalse, YesNo, YesNoChoices } from '../../../types.js'; +import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSRegions, BasePaths, BundleType, GatewayTypes, InstallationFlowMethods, PublicDockerRepoBaseUrl, PublicRepoUrl, TrueFalse, YesNo, YesNoChoices } from '../../../types.js'; import { askInput, askList, validateInputLength, validateRegex } from '../../basic-prompts.js'; import { isWindows, writeTemplates, writeToFile } from '../../utils.js'; import { AWSAgentValues } from '../index.js'; @@ -80,11 +80,9 @@ export const AWSPrompts = { AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', - ENTER_MORE_COGNITO_USER_POOLS: 'Do you want to enter another Cognito User Pool for Agent Core Gateway mode?', - COGNITO: 'Enter the List of AWS Cognito user pools used for authentication in Agent Core Gateway mode', + ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for Agent Core Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in Agent Core Gateway mode', COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', - ASK_COGNITO_REGION: 'Do you want to specify a region for the Cognito User Pool? (If not, the agent will use the same region as the gateway)', - COGNITO_REGION: 'Select the AWS region of the Cognito user pool. Defaults to the agent region if omitted', }; export const askBundleType = async (): Promise => { @@ -337,7 +335,7 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr choices: YesNoChoices, })) === YesNo.Yes; installConfig.log(chalk.gray(AWSPrompts.COGNITO)); - const cognitoUserPools: AWSCognitoConfig[] = []; + const cognitoUserPoolIDs: string[] = []; let askCognitoUserPools = true; while (askCognitoUserPools) { @@ -345,35 +343,16 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr msg: AWSPrompts.COGNITO_USER_POOL_ID, })) as string; - const askRegion = (await askList({ - msg: AWSPrompts.ASK_COGNITO_REGION, - default: YesNo.No, - choices: YesNoChoices, - })) === YesNo.Yes; - - if (askRegion) { - - const regions = Object.values(AWSRegions).map((str) => ({ name: str, value: str })); - - const region = await askList({ - msg: AWSPrompts.COGNITO_REGION, - choices: regions, - - }); - - cognitoUserPools.push({ userPoolId, region }); - } else { - cognitoUserPools.push({ userPoolId, region: awsAgentValues.region }); - } + cognitoUserPoolIDs.push(userPoolId); askCognitoUserPools = await askList({ - msg: AWSPrompts.ENTER_MORE_COGNITO_USER_POOLS, + msg: AWSPrompts.ENTER_MORE_COGNITO_USER_POOL_IDS, choices: YesNoChoices, default: YesNo.No, }) === YesNo.Yes; } - awsAgentValues.cognito = cognitoUserPools; + awsAgentValues.cognitoUserPoolIDs = cognitoUserPoolIDs; } // set agent versions diff --git a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts index 74c9fb79..b088e659 100644 --- a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import logger from '../../../../logger.js'; import { ApiServerClient } from '../../../clients-external/apiserverclient.js'; import { DefinitionsManager } from '../../../results/DefinitionsManager.js'; -import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSAgentCoreConfig, AWSCognitoConfig, AWSGatewayMode, AWSRegions, BundleType, GatewayTypes, InstallationFlowMethods, SaaSGatewayTypes, YesNo, YesNoChoices } from '../../../types.js'; +import { AgentConfigTypes, AgentInstallConfig, AgentNames, AgentTypes, AWSAgentCoreConfig, AWSGatewayMode, BundleType, GatewayTypes, InstallationFlowMethods, SaaSGatewayTypes, YesNo, YesNoChoices } from '../../../types.js'; import { askInput, askList, validateInputLength, validateRegex } from '../../basic-prompts.js'; import * as helpers from '../index.js'; import { @@ -25,15 +25,15 @@ class AWSDataplaneConfig extends DataplaneConfig { stageTagName: string; gatewayMode: AWSGatewayMode; agentCore: AWSAgentCoreConfig; - cognito: AWSCognitoConfig[]; + cognitoUserPoolIDs: string[]; - constructor(arn: string, enableFullTransactionLogging: boolean, stageTagName: string, agentCoreConfig: AWSAgentCoreConfig, cognitoConfig: AWSCognitoConfig[]) { + constructor(arn: string, enableFullTransactionLogging: boolean, stageTagName: string, agentCoreConfig: AWSAgentCoreConfig, cognitoUserPoolIDs: string[]) { super('AWS'); this.accessLogARN = arn; this.fullTransactionLogging = enableFullTransactionLogging; this.stageTagName = stageTagName; this.agentCore = agentCoreConfig; - this.cognito = cognitoConfig; + this.cognitoUserPoolIDs = cognitoUserPoolIDs; } } @@ -54,7 +54,7 @@ class SaasAWSAgentValues extends SaasAgentValues { stageTagName: string; agentCoreGatewayMode: boolean; agentCore: AWSAgentCoreConfig; - cognito: AWSCognitoConfig[]; + cognitoUserPoolIDs: string[]; constructor() { super(); @@ -68,7 +68,7 @@ class SaasAWSAgentValues extends SaasAgentValues { this.fullTransactionLogging = false; this.stageTagName = ''; this.agentCore = {} as AWSAgentCoreConfig; - this.cognito = [] as AWSCognitoConfig[]; + this.cognitoUserPoolIDs = [] as string[]; } override getAccessData(): string { if (this.authType === AWSAuthType.KEYS) { @@ -99,11 +99,9 @@ const SaasPrompts = { AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', - ENTER_MORE_COGNITO_USER_POOLS: 'Do you want to enter another Cognito User Pool for Agent Core Gateway mode?', - COGNITO: 'Enter the List of AWS Cognito user pools used for authentication in Agent Core Gateway mode', + ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for Agent Core Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in Agent Core Gateway mode', COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', - ASK_COGNITO_REGION: 'Do you want to specify a region for the Cognito User Pool? (If not, the agent will use the same region as the gateway)', - COGNITO_REGION: 'Select the AWS region of the Cognito user pool. Defaults to the agent region if omitted', }; export const askBundleType = async (): Promise => { @@ -192,7 +190,7 @@ const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: choices: YesNoChoices, })) === YesNo.Yes; log(chalk.gray(SaasPrompts.COGNITO)); - const cognitoUserPools: AWSCognitoConfig[] = []; + const cognitoUserPoolIDs: string[] = []; let askCognitoUserPools = true; while (askCognitoUserPools) { @@ -200,35 +198,16 @@ const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: msg: SaasPrompts.COGNITO_USER_POOL_ID, })) as string; - const askRegion = (await askList({ - msg: SaasPrompts.ASK_COGNITO_REGION, - default: YesNo.No, - choices: YesNoChoices, - })) === YesNo.Yes; - - if (askRegion) { - - const regions = Object.values(AWSRegions).map((str) => ({ name: str, value: str })); - - const region = await askList({ - msg: SaasPrompts.COGNITO_REGION, - choices: regions, - - }); - - cognitoUserPools.push({ userPoolId, region }); - } else { - cognitoUserPools.push({ userPoolId, region: agentValues.region }); - } + cognitoUserPoolIDs.push(userPoolId); askCognitoUserPools = await askList({ - msg: SaasPrompts.ENTER_MORE_COGNITO_USER_POOLS, + msg: SaasPrompts.ENTER_MORE_COGNITO_USER_POOL_IDS, choices: YesNoChoices, default: YesNo.No, }) === YesNo.Yes; } - agentValues.cognito = cognitoUserPools; + agentValues.cognitoUserPoolIDs = cognitoUserPoolIDs; } @@ -310,7 +289,7 @@ export const completeInstall = async ( awsAgentValues.fullTransactionLogging, awsAgentValues.stageTagName, awsAgentValues.agentCore, - awsAgentValues.cognito + awsAgentValues.cognitoUserPoolIDs ); } else { dataplaneConfig = new DataplaneConfig('AWS'); diff --git a/src/lib/engage/utils/agents/templates/awsTemplates.ts b/src/lib/engage/utils/agents/templates/awsTemplates.ts index 8fe52123..ea62bd97 100644 --- a/src/lib/engage/utils/agents/templates/awsTemplates.ts +++ b/src/lib/engage/utils/agents/templates/awsTemplates.ts @@ -1,6 +1,5 @@ import { AWSAgentCoreConfig, - AWSCognitoConfig, CentralAgentConfig, CloudFormationConfig, TraceabilityConfig, @@ -23,7 +22,7 @@ export class AWSAgentValues { cloudFormationConfig: CloudFormationConfig; agentCoreGatewayMode: boolean; agentCore: AWSAgentCoreConfig; - cognito: AWSCognitoConfig[]; + cognitoUserPoolIDs: string[]; constructor(awsDeployment: string) { this.accessKey = awsDeployment === 'Other' ? '**Insert Access Key**' : ''; @@ -38,7 +37,7 @@ export class AWSAgentValues { this.cloudFormationConfig = new CloudFormationConfig(); this.agentCoreGatewayMode = false; this.agentCore = new AWSAgentCoreConfig(); - this.cognito = []; + this.cognitoUserPoolIDs = []; } updateCloudFormationConfig = () => { @@ -114,9 +113,8 @@ AWS_STAGETAGNAME={{stageTagName}} AWS_GATEWAYMODE=agentcore-gateway AWS_AGENTCORE_LOGGROUPPREFIX={{agentCore.logGroupPrefix}} AWS_AGENTCORE_IAMAUTHENABLED={{agentCore.iamAuthEnabled}} -{{#each cognito}} -AWS_COGNITO_USERPOOLID_{{add @index 1}}={{this.userPoolId}} -AWS_COGNITO_REGION_{{add @index 1}}={{this.region}} +{{#each cognitoUserPoolIDs}} +AWS_COGNITO_USERPOOLID_{{add @index 1}}={{this}} {{/each}} {{else}} AWS_GATEWAYMODE=api-gateway diff --git a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js index a0533fe1..d933b77c 100644 --- a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js +++ b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js @@ -233,7 +233,6 @@ describe('AWS on-prem agent flow', () => { 'No', // fullTransactionLogging 'Yes', // AGENT_CORE_GATEWAY_MODE 'Yes', // iamAuthEnabled - 'No', // askRegion? (use agent region) 'No', // enterMore? ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -254,11 +253,10 @@ describe('AWS on-prem agent flow', () => { expect(result.agentCoreGatewayMode).to.equal(true); expect(result.agentCore.logGroupPrefix).to.equal('/aws/prefix'); expect(result.agentCore.iamAuthEnabled).to.equal(true); - expect(result.cognito).to.have.length(1); - expect(result.cognito[0].userPoolId).to.equal('us-east-1_123456789'); - expect(result.cognito[0].region).to.equal('us-east-1'); + expect(result.cognitoUserPoolIDs).to.have.length(1); + expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_123456789'); expect(td.explain(promptStubs.askInput).callCount).to.equal(7); - expect(td.explain(promptStubs.askList).callCount).to.equal(7); + expect(td.explain(promptStubs.askList).callCount).to.equal(6); }); it('enables agent core gateway mode and collects multiple cognito pools', async () => { @@ -268,9 +266,7 @@ describe('AWS on-prem agent flow', () => { 'No', // fullTransactionLogging 'Yes', // AGENT_CORE_GATEWAY_MODE 'No', // iamAuthEnabled - 'No', // askRegion? pool 1 'Yes', // enterMore? (add another pool) - 'No', // askRegion? pool 2 'No', // enterMore? ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -292,13 +288,11 @@ describe('AWS on-prem agent flow', () => { expect(result.agentCoreGatewayMode).to.equal(true); expect(result.agentCore.logGroupPrefix).to.equal(''); expect(result.agentCore.iamAuthEnabled).to.equal(false); - expect(result.cognito).to.have.length(2); - expect(result.cognito[0].userPoolId).to.equal('us-east-1_111111111'); - expect(result.cognito[0].region).to.equal('us-east-1'); - expect(result.cognito[1].userPoolId).to.equal('eu-west-1_222222222'); - expect(result.cognito[1].region).to.equal('us-east-1'); + expect(result.cognitoUserPoolIDs).to.have.length(2); + expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_111111111'); + expect(result.cognitoUserPoolIDs[1]).to.equal('eu-west-1_222222222'); expect(td.explain(promptStubs.askInput).callCount).to.equal(8); - expect(td.explain(promptStubs.askList).callCount).to.equal(9); + expect(td.explain(promptStubs.askList).callCount).to.equal(7); }); it('stops question flow when AWS region lookup fails', async () => { @@ -436,7 +430,7 @@ function createHelpersStubs() { this.stageTagName = ''; this.agentCoreGatewayMode = false; this.agentCore = { logGroupPrefix: '', iamAuthEnabled: false }; - this.cognito = []; + this.cognitoUserPoolIDs = []; this.cloudFormationConfig = { APIGWCWRoleSetup: '', APIGWTrafficLogGroupName: '/aws/apigw/logs', @@ -466,12 +460,6 @@ function createHelpersStubs() { return { AWSAgentValues, - AWSCognitoConfig: class AWSCognitoConfig { - constructor(userPoolId, region) { - this.userPoolId = userPoolId; - this.region = region; - } - }, AWSAgentCoreConfig: class AWSAgentCoreConfig { constructor(logGroupPrefix, iamAuthEnabled) { this.logGroupPrefix = logGroupPrefix ?? ''; diff --git a/test/lib/engage/services/install-service/saas/test-aws.saas.js b/test/lib/engage/services/install-service/saas/test-aws.saas.js index de00d2ac..3673ff68 100644 --- a/test/lib/engage/services/install-service/saas/test-aws.saas.js +++ b/test/lib/engage/services/install-service/saas/test-aws.saas.js @@ -173,7 +173,6 @@ describe('AWS SaaS agent flow', () => { engageTypes.YesNo.Yes, engageTypes.YesNo.No, engageTypes.YesNo.No, - engageTypes.YesNo.No, ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -181,9 +180,8 @@ describe('AWS SaaS agent flow', () => { expect(result.agentCoreGatewayMode).to.equal(true); expect(result.agentCore.logGroupPrefix).to.equal('/aws/prefix'); expect(result.agentCore.iamAuthEnabled).to.equal(true); - expect(result.cognito).to.have.length(1); - expect(result.cognito[0].userPoolId).to.equal('us-east-1_123456789'); - expect(result.cognito[0].region).to.equal('us-east-1'); + expect(result.cognitoUserPoolIDs).to.have.length(1); + expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_123456789'); expect(result.fullTransactionLogging).to.equal(false); }); @@ -194,7 +192,7 @@ describe('AWS SaaS agent flow', () => { fullTransactionLogging: true, stageTagName: 'stage-tag', agentCore: { logGroupPrefix: '/aws/prefix', iamAuthEnabled: true }, - cognito: [ { userPoolId: 'us-east-1_123456789', region: 'us-east-1' } ], + cognitoUserPoolIDs: [ 'us-east-1_123456789' ], redaction: {}, }; @@ -203,7 +201,7 @@ describe('AWS SaaS agent flow', () => { expect(dataplaneArg.type).to.equal('AWS'); expect(dataplaneArg.accessLogARN).to.contain('arn:aws:logs'); expect(dataplaneArg.agentCore).to.deep.equal({ logGroupPrefix: '/aws/prefix', iamAuthEnabled: true }); - expect(dataplaneArg.cognito).to.deep.equal([ { userPoolId: 'us-east-1_123456789', region: 'us-east-1' } ]); + expect(dataplaneArg.cognitoUserPoolIDs).to.deep.equal([ 'us-east-1_123456789' ]); }); it('passes IDP config in completeInstall context', async () => { From 2c76cba8a72e57c1fc2fbac72105875cf86db8de Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Wed, 15 Jul 2026 09:47:12 -0700 Subject: [PATCH 3/6] APIGOV-32905 Updates --- .../engage/utils/agents/flows/awsAgents.ts | 122 ++++++++---------- .../utils/agents/flows/awsSaasAgents.ts | 12 +- .../utils/agents/templates/awsTemplates.ts | 4 +- .../on-prem/test-aws.onprem.js | 4 +- .../install-service/saas/test-aws.saas.js | 2 +- 5 files changed, 67 insertions(+), 77 deletions(-) diff --git a/src/lib/engage/utils/agents/flows/awsAgents.ts b/src/lib/engage/utils/agents/flows/awsAgents.ts index d5d6ecad..0d14110b 100644 --- a/src/lib/engage/utils/agents/flows/awsAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsAgents.ts @@ -77,12 +77,12 @@ export const AWSPrompts = { FULL_TRANSACTION_LOGGING: 'Do you want to enable Full Transaction Logging? Please note that CloudWatch costs would increase when Full Transaction Logging is enabled', TA_QUEUE: 'Enter the traceability queue name', VPC_ID: 'Enter the VPC ID to deploy the EC2 instance to. Leave blank to create entire infrastructure', - AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', - AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', - AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', - ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for Agent Core Gateway mode?', - COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in Agent Core Gateway mode', - COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', + AGENT_CORE_GATEWAY_MODE: 'Do you want to enable AgentCore Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', + AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the AgentCore Gateway vendored logs', + AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for AgentCore Gateway requests?', + ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for AgentCore Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in AgentCore Gateway mode', + COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the AgentCore will use for authentication', }; export const askBundleType = async (): Promise => { @@ -282,40 +282,7 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr // AWS Region awsAgentValues.region = await helpers.askAWSRegion(); - // S3 bucket - awsAgentValues.cloudFormationConfig.AgentResourcesBucket = (await askInput({ - msg: AWSPrompts.S3_BUCKET, - validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP, InvalidMsg.S3_BUCKET), - })) as string; - - // APIGWCWRoleSetup - awsAgentValues.cloudFormationConfig.APIGWCWRoleSetup = await askToCreateRoleSetup(); - - // APIGWTrafficLogGroupName - const apiGWTrafficLogGroupName = (await askInput({ - msg: AWSPrompts.APIGW_LOG_GROUP, - defaultValue: awsAgentValues.cloudFormationConfig.APIGWTrafficLogGroupName, - validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), - })) as string; - awsAgentValues.logGroup = apiGWTrafficLogGroupName; - awsAgentValues.cloudFormationConfig.APIGWTrafficLogGroupName = apiGWTrafficLogGroupName; - - // StageTagName - const stageTagName = (await askInput({ - msg: AWSPrompts.STAGE_TAG_NAME, - validate: validateInputLength(STAGE_TAG_NAME_LENGTH, 'Maximum length of \'stage tag name\' is 127'), - })) as string; - awsAgentValues.stageTagName = stageTagName; - - // FullTransactionLogging - const fullTransactionLogging = ((await askList({ - msg: AWSPrompts.FULL_TRANSACTION_LOGGING, - choices: YesNoChoices, - default: YesNo.No, - })) === YesNo.Yes); - - awsAgentValues.fullTransactionLogging = fullTransactionLogging; - + // Determine gateway mode early to skip irrelevant API GW prompts awsAgentValues.agentCoreGatewayMode = (await askList({ msg: AWSPrompts.AGENT_CORE_GATEWAY_MODE, default: YesNo.No, @@ -353,37 +320,60 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr } awsAgentValues.cognitoUserPoolIDs = cognitoUserPoolIDs; - } + } else { + // API Gateway mode — collect all API GW-specific configuration + awsAgentValues.cloudFormationConfig.AgentResourcesBucket = (await askInput({ + msg: AWSPrompts.S3_BUCKET, + validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP, InvalidMsg.S3_BUCKET), + })) as string; - // set agent versions - awsAgentValues.cloudFormationConfig.DiscoveryAgentVersion = installConfig.daVersion; - awsAgentValues.cloudFormationConfig.TraceabilityAgentVersion = installConfig.taVersion; + awsAgentValues.cloudFormationConfig.APIGWCWRoleSetup = await askToCreateRoleSetup(); - // Configure appropriate Gateway type - switch (awsAgentValues.cloudFormationConfig.DeploymentType) { - case DeploymentTypes.ECS_FARGATE: { - awsAgentValues = await configureECSDeployment(awsAgentValues); - break; - } - case DeploymentTypes.EC2: { - awsAgentValues = await configureEC2Deployment(awsAgentValues, installConfig.log); - break; + const apiGWTrafficLogGroupName = (await askInput({ + msg: AWSPrompts.APIGW_LOG_GROUP, + defaultValue: awsAgentValues.cloudFormationConfig.APIGWTrafficLogGroupName, + validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), + })) as string; + awsAgentValues.logGroup = apiGWTrafficLogGroupName; + awsAgentValues.cloudFormationConfig.APIGWTrafficLogGroupName = apiGWTrafficLogGroupName; + + awsAgentValues.stageTagName = (await askInput({ + msg: AWSPrompts.STAGE_TAG_NAME, + validate: validateInputLength(STAGE_TAG_NAME_LENGTH, 'Maximum length of \'stage tag name\' is 127'), + })) as string; + + awsAgentValues.fullTransactionLogging = ((await askList({ + msg: AWSPrompts.FULL_TRANSACTION_LOGGING, + choices: YesNoChoices, + default: YesNo.No, + })) === YesNo.Yes); + + awsAgentValues.cloudFormationConfig.DiscoveryAgentVersion = installConfig.daVersion; + awsAgentValues.cloudFormationConfig.TraceabilityAgentVersion = installConfig.taVersion; + + switch (awsAgentValues.cloudFormationConfig.DeploymentType) { + case DeploymentTypes.ECS_FARGATE: { + awsAgentValues = await configureECSDeployment(awsAgentValues); + break; + } + case DeploymentTypes.EC2: { + awsAgentValues = await configureEC2Deployment(awsAgentValues, installConfig.log); + break; + } } - } - // DiscoveryAgentLogGroupName - awsAgentValues.cloudFormationConfig.DiscoveryAgentLogGroupName = (await askInput({ - msg: AWSPrompts.DA_LOG_GROUP, - defaultValue: awsAgentValues.cloudFormationConfig.DiscoveryAgentLogGroupName, - validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), - })) as string; + awsAgentValues.cloudFormationConfig.DiscoveryAgentLogGroupName = (await askInput({ + msg: AWSPrompts.DA_LOG_GROUP, + defaultValue: awsAgentValues.cloudFormationConfig.DiscoveryAgentLogGroupName, + validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), + })) as string; - // TraceabilityAgentLogGroupName - awsAgentValues.cloudFormationConfig.TraceabilityAgentLogGroupName = (await askInput({ - msg: AWSPrompts.TA_LOG_GROUP, - defaultValue: awsAgentValues.cloudFormationConfig.TraceabilityAgentLogGroupName, - validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), - })) as string; + awsAgentValues.cloudFormationConfig.TraceabilityAgentLogGroupName = (await askInput({ + msg: AWSPrompts.TA_LOG_GROUP, + defaultValue: awsAgentValues.cloudFormationConfig.TraceabilityAgentLogGroupName, + validate: validateRegex(helpers.AWSRegexPatterns.AWS_REGEXP_LOG_GROUP_NAME, InvalidMsg.LOG_GROUP), + })) as string; + } return awsAgentValues; }; diff --git a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts index b088e659..bca46644 100644 --- a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts @@ -96,12 +96,12 @@ const SaasPrompts = { ACCESS_LOG_ARN: 'Enter the ARN for the Access Log that the Discovery will add and the Traceability will use', STAGE_TAG_NAME: 'Enter the name of the tag on AWS API Gateway Stage that holds mapped stage on Amplify Engage', FULL_TRANSACTION_LOGGING: 'Do you want to enable Full Transaction Logging? Please note that CloudWatch costs would increase when Full Transaction Logging is enabled', - AGENT_CORE_GATEWAY_MODE: 'Do you want to enable Agent Core Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', - AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the Agent Core Gateway vendored logs', - AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for Agent Core Gateway requests?', - ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for Agent Core Gateway mode?', - COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in Agent Core Gateway mode', - COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the Agent Core will use for authentication', + AGENT_CORE_GATEWAY_MODE: 'Do you want to enable AgentCore Gateway Mode? (If not, the default will be to run the agent in API Gateway mode)', + AGENT_CORE_LOG_GROUP_PREFIX: 'Enter the prefix for the AgentCore Gateway vendored logs', + AGENT_CORE_IAM_AUTH: 'Do you want to enable IAM Authentication for AgentCore Gateway requests?', + ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for AgentCore Gateway mode?', + COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in AgentCore Gateway mode', + COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the AgentCore will use for authentication', }; export const askBundleType = async (): Promise => { diff --git a/src/lib/engage/utils/agents/templates/awsTemplates.ts b/src/lib/engage/utils/agents/templates/awsTemplates.ts index ea62bd97..58ea94bc 100644 --- a/src/lib/engage/utils/agents/templates/awsTemplates.ts +++ b/src/lib/engage/utils/agents/templates/awsTemplates.ts @@ -107,8 +107,6 @@ AWS_AUTH_ACCESSKEY={{accessKey}} {{#if secretKey}} AWS_AUTH_SECRETKEY={{secretKey}} {{/if}} -AWS_LOGGROUP={{logGroup}} -AWS_STAGETAGNAME={{stageTagName}} {{#if agentCoreGatewayMode}} AWS_GATEWAYMODE=agentcore-gateway AWS_AGENTCORE_LOGGROUPPREFIX={{agentCore.logGroupPrefix}} @@ -117,6 +115,8 @@ AWS_AGENTCORE_IAMAUTHENABLED={{agentCore.iamAuthEnabled}} AWS_COGNITO_USERPOOLID_{{add @index 1}}={{this}} {{/each}} {{else}} +AWS_LOGGROUP={{logGroup}} +AWS_STAGETAGNAME={{stageTagName}} AWS_GATEWAYMODE=api-gateway {{/if}} diff --git a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js index d933b77c..0e3ff97e 100644 --- a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js +++ b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js @@ -226,7 +226,7 @@ describe('AWS on-prem agent flow', () => { expect(td.explain(promptStubs.askList).callCount).to.equal(4); }); - it('enables agent core gateway mode and collects a single cognito pool', async () => { + it('enables agentcore gateway mode and collects a single cognito pool', async () => { const askListResponses = [ flowModule.DeploymentTypes.OTHER, 'Yes', // APIGWCWRoleSetup @@ -259,7 +259,7 @@ describe('AWS on-prem agent flow', () => { expect(td.explain(promptStubs.askList).callCount).to.equal(6); }); - it('enables agent core gateway mode and collects multiple cognito pools', async () => { + it('enables agentcore gateway mode and collects multiple cognito pools', async () => { const askListResponses = [ flowModule.DeploymentTypes.OTHER, 'Yes', // APIGWCWRoleSetup diff --git a/test/lib/engage/services/install-service/saas/test-aws.saas.js b/test/lib/engage/services/install-service/saas/test-aws.saas.js index 3673ff68..62f24653 100644 --- a/test/lib/engage/services/install-service/saas/test-aws.saas.js +++ b/test/lib/engage/services/install-service/saas/test-aws.saas.js @@ -156,7 +156,7 @@ describe('AWS SaaS agent flow', () => { expect(result.redaction.maskingCharacter).to.equal('***'); }); - it('collects agent core gateway mode with IAM auth enabled and a single cognito pool', async () => { + it('collects agentcore gateway mode with IAM auth enabled and a single cognito pool', async () => { const askInputResponses = [ 'arn:aws:iam::000000000000:role/name-of-role', 'external-id', From 6078699aa0b106cd64329e39235518262fb7fead Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Tue, 21 Jul 2026 16:20:19 -0700 Subject: [PATCH 4/6] APIGOV-32905 Updated AWS TA agent workflow for agent core mode --- src/lib/engage/types.ts | 6 +- .../engage/utils/agents/flows/awsAgents.ts | 27 ++++-- .../utils/agents/flows/awsSaasAgents.ts | 34 +++++-- .../utils/agents/templates/awsTemplates.ts | 9 +- .../on-prem/test-aws.onprem.js | 90 +++++++++++-------- .../install-service/saas/test-aws.saas.js | 40 +++++++-- 6 files changed, 149 insertions(+), 57 deletions(-) diff --git a/src/lib/engage/types.ts b/src/lib/engage/types.ts index 9eb168f8..4ec6192b 100644 --- a/src/lib/engage/types.ts +++ b/src/lib/engage/types.ts @@ -760,10 +760,14 @@ export enum AWSGatewayMode { export class AWSAgentCoreConfig { logGroupPrefix?: string; iamAuthEnabled?: boolean; + cloudTrailEnabled?: boolean; + cloudTrailBucket?: string; - constructor(logGroupPrefix?: string, iamAuthEnabled?: boolean) { + constructor(logGroupPrefix?: string, iamAuthEnabled?: boolean, cloudTrailEnabled?: boolean, cloudTrailBucket?: string) { this.logGroupPrefix = logGroupPrefix ?? ''; this.iamAuthEnabled = iamAuthEnabled ?? false; + this.cloudTrailEnabled = cloudTrailEnabled ?? false; + this.cloudTrailBucket = cloudTrailBucket ?? ''; } } diff --git a/src/lib/engage/utils/agents/flows/awsAgents.ts b/src/lib/engage/utils/agents/flows/awsAgents.ts index 0d14110b..78d09265 100644 --- a/src/lib/engage/utils/agents/flows/awsAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsAgents.ts @@ -83,6 +83,8 @@ export const AWSPrompts = { ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for AgentCore Gateway mode?', COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in AgentCore Gateway mode', COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the AgentCore will use for authentication', + AGENTCORE_CLOUDTRAILENABLED: 'Do you want to enable CloudTrail-based consumer attribution for Cognito gateway?', + AGENTCORE_CLOUDTRAILBUCKET: 'Enter the name of the S3 bucket that stores the CloudTrail data-event logs' }; export const askBundleType = async (): Promise => { @@ -290,12 +292,6 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr })) === YesNo.Yes; if (awsAgentValues.agentCoreGatewayMode) { - awsAgentValues.agentCore.logGroupPrefix = (await askInput({ - msg: AWSPrompts.AGENT_CORE_LOG_GROUP_PREFIX, - defaultValue: awsAgentValues.agentCore.logGroupPrefix !== '' ? awsAgentValues.agentCore.logGroupPrefix : undefined, - allowEmptyInput: true, - })) as string; - awsAgentValues.agentCore.iamAuthEnabled = (await askList({ msg: AWSPrompts.AGENT_CORE_IAM_AUTH, default: YesNo.No, @@ -320,6 +316,25 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr } awsAgentValues.cognitoUserPoolIDs = cognitoUserPoolIDs; + if (installConfig.switches.isTaEnabled) { + awsAgentValues.agentCore.logGroupPrefix = (await askInput({ + msg: AWSPrompts.AGENT_CORE_LOG_GROUP_PREFIX, + defaultValue: awsAgentValues.agentCore.logGroupPrefix !== '' ? awsAgentValues.agentCore.logGroupPrefix : undefined, + allowEmptyInput: true, + })) as string; + + awsAgentValues.agentCore.cloudTrailEnabled = (await askList({ + msg: AWSPrompts.AGENTCORE_CLOUDTRAILENABLED, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + awsAgentValues.agentCore.cloudTrailBucket = (await askInput({ + msg: AWSPrompts.AGENTCORE_CLOUDTRAILBUCKET, + defaultValue: awsAgentValues.agentCore.cloudTrailBucket !== '' ? awsAgentValues.agentCore.cloudTrailBucket : undefined, + allowEmptyInput: false, + })) as string; + } } else { // API Gateway mode — collect all API GW-specific configuration awsAgentValues.cloudFormationConfig.AgentResourcesBucket = (await askInput({ diff --git a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts index bca46644..2ca8e6e2 100644 --- a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts @@ -102,6 +102,8 @@ const SaasPrompts = { ENTER_MORE_COGNITO_USER_POOL_IDS: 'Do you want to enter another Cognito User Pool ID for AgentCore Gateway mode?', COGNITO: 'Enter the List of AWS Cognito user pool IDs used for authentication in AgentCore Gateway mode', COGNITO_USER_POOL_ID: 'Enter the User Pool ID for the Cognito User Pool the AgentCore will use for authentication', + AGENTCORE_CLOUDTRAILENABLED: 'Do you want to enable CloudTrail-based consumer attribution for Cognito gateway?', + AGENTCORE_CLOUDTRAILBUCKET: 'Enter the name of the S3 bucket that stores the CloudTrail data-event logs' }; export const askBundleType = async (): Promise => { @@ -170,7 +172,7 @@ const askForAWSCredentials = async (agentValues: SaasAWSAgentValues, log: (text: return agentValues; }; -const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: (text: string) => void = () => {}): Promise => { +const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, installConfig: AgentInstallConfig): Promise => { agentValues.agentCoreGatewayMode = (await askList({ msg: SaasPrompts.AGENT_CORE_GATEWAY_MODE, default: YesNo.No, @@ -178,18 +180,12 @@ const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: })) === YesNo.Yes; if (agentValues.agentCoreGatewayMode) { - agentValues.agentCore.logGroupPrefix = (await askInput({ - msg: SaasPrompts.AGENT_CORE_LOG_GROUP_PREFIX, - defaultValue: agentValues.agentCore.logGroupPrefix !== '' ? agentValues.agentCore.logGroupPrefix : undefined, - allowEmptyInput: true, - })) as string; - agentValues.agentCore.iamAuthEnabled = (await askList({ msg: SaasPrompts.AGENT_CORE_IAM_AUTH, default: YesNo.No, choices: YesNoChoices, })) === YesNo.Yes; - log(chalk.gray(SaasPrompts.COGNITO)); + installConfig.log(chalk.gray(SaasPrompts.COGNITO)); const cognitoUserPoolIDs: string[] = []; let askCognitoUserPools = true; @@ -209,6 +205,26 @@ const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, log: agentValues.cognitoUserPoolIDs = cognitoUserPoolIDs; + if (installConfig.switches.isTaEnabled) { + agentValues.agentCore.logGroupPrefix = (await askInput({ + msg: SaasPrompts.AGENT_CORE_LOG_GROUP_PREFIX, + defaultValue: agentValues.agentCore.logGroupPrefix !== '' ? agentValues.agentCore.logGroupPrefix : undefined, + allowEmptyInput: true, + })) as string; + + agentValues.agentCore.cloudTrailEnabled = (await askList({ + msg: SaasPrompts.AGENTCORE_CLOUDTRAILENABLED, + default: YesNo.No, + choices: YesNoChoices, + })) === YesNo.Yes; + + agentValues.agentCore.cloudTrailBucket = (await askInput({ + msg: SaasPrompts.AGENTCORE_CLOUDTRAILBUCKET, + defaultValue: agentValues.agentCore.cloudTrailBucket !== '' ? agentValues.agentCore.cloudTrailBucket : undefined, + allowEmptyInput: false, + })) as string; + } + } return agentValues; @@ -233,7 +249,7 @@ export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Pr validate: validateInputLength(STAGE_TAG_NAME_LENGTH, 'Maximum length of \'stage tag name\' is 127'), })) as string; - agentValues = await askForAgentCoreGatewayMode(awsValues, installConfig.log); + agentValues = await askForAgentCoreGatewayMode(awsValues, installConfig); if (installConfig.switches.isTaEnabled) { installConfig.log(chalk.gray('\nThe access log ARN is a cloud watch log group amazon resource name')); diff --git a/src/lib/engage/utils/agents/templates/awsTemplates.ts b/src/lib/engage/utils/agents/templates/awsTemplates.ts index 58ea94bc..d3db3907 100644 --- a/src/lib/engage/utils/agents/templates/awsTemplates.ts +++ b/src/lib/engage/utils/agents/templates/awsTemplates.ts @@ -67,6 +67,14 @@ AWS_AUTH_SECRETKEY={{secretKey}} {{#if fullTransactionLogging}} AWS_FULLTRANSACTIONLOGGING={{fullTransactionLogging}} {{/if}} +{{#if agentCoreGatewayMode}} +AWS_GATEWAYMODE=agentcore-gateway +AWS_AGENTCORE_LOGGROUPPREFIX={{agentCore.logGroupPrefix}} +AWS_AGENTCORE_CLOUDTRAILENABLED={{agentCore.cloudTrailEnabled}} +{{#if agentCore.cloudTrailEnabled}} +AWS_AGENTCORE_CLOUDTRAILBUCKET={{agentCore.cloudTrailBucket}} +{{/if }} +{{/if}} # Amplify Central configs {{#if traceabilityConfig.usageReportingOffline}} @@ -109,7 +117,6 @@ AWS_AUTH_SECRETKEY={{secretKey}} {{/if}} {{#if agentCoreGatewayMode}} AWS_GATEWAYMODE=agentcore-gateway -AWS_AGENTCORE_LOGGROUPPREFIX={{agentCore.logGroupPrefix}} AWS_AGENTCORE_IAMAUTHENABLED={{agentCore.iamAuthEnabled}} {{#each cognitoUserPoolIDs}} AWS_COGNITO_USERPOOLID_{{add @index 1}}={{this}} diff --git a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js index 0e3ff97e..b7ac0b06 100644 --- a/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js +++ b/test/lib/engage/services/install-service/on-prem/test-aws.onprem.js @@ -92,11 +92,11 @@ describe('AWS on-prem agent flow', () => { it('collects EC2 values and includes VPC-derived prompts when VPC is set', async () => { const askListResponses = [ flowModule.DeploymentTypes.EC2, - 'Yes', - 'No', - 'No', + 'No', // AGENT_CORE_GATEWAY_MODE + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging 't3.micro', - 'Yes', + 'Yes', // PUBLIC_IP ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -133,9 +133,9 @@ describe('AWS on-prem agent flow', () => { it('skips VPC-derived prompts when EC2 VPC is empty', async () => { const askListResponses = [ flowModule.DeploymentTypes.EC2, - 'Yes', - 'No', - 'No', + 'No', // AGENT_CORE_GATEWAY_MODE + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging 't3.micro', ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -166,9 +166,9 @@ describe('AWS on-prem agent flow', () => { it('collects ECS-only deployment prompts', async () => { const askListResponses = [ flowModule.DeploymentTypes.ECS_FARGATE, - 'Yes', - 'No', - 'No', + 'No', // AGENT_CORE_GATEWAY_MODE + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -200,9 +200,9 @@ describe('AWS on-prem agent flow', () => { it('collects minimal prompts for OTHER deployment type', async () => { const askListResponses = [ flowModule.DeploymentTypes.OTHER, - 'Yes', - 'No', - 'No', + 'No', // AGENT_CORE_GATEWAY_MODE + 'Yes', // APIGWCWRoleSetup + 'No', // fullTransactionLogging ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -229,22 +229,17 @@ describe('AWS on-prem agent flow', () => { it('enables agentcore gateway mode and collects a single cognito pool', async () => { const askListResponses = [ flowModule.DeploymentTypes.OTHER, - 'Yes', // APIGWCWRoleSetup - 'No', // fullTransactionLogging 'Yes', // AGENT_CORE_GATEWAY_MODE 'Yes', // iamAuthEnabled 'No', // enterMore? + 'Yes', // AGENTCORE_CLOUDTRAILENABLED ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); const askInputResponses = [ - 'agents-bucket', - '/aws/apigw/logs', - 'stage-tag', - '/aws/prefix', 'us-east-1_123456789', - '/aws/da/logs', - '/aws/ta/logs', + '/aws/prefix', + 'my-cloudtrail-bucket', ]; td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); @@ -255,31 +250,52 @@ describe('AWS on-prem agent flow', () => { expect(result.agentCore.iamAuthEnabled).to.equal(true); expect(result.cognitoUserPoolIDs).to.have.length(1); expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_123456789'); - expect(td.explain(promptStubs.askInput).callCount).to.equal(7); - expect(td.explain(promptStubs.askList).callCount).to.equal(6); + expect(result.agentCore.cloudTrailEnabled).to.equal(true); + expect(result.agentCore.cloudTrailBucket).to.equal('my-cloudtrail-bucket'); + expect(td.explain(promptStubs.askInput).callCount).to.equal(3); + expect(td.explain(promptStubs.askList).callCount).to.equal(5); + }); + + it('skips the log group prefix and CloudTrail prompts when TA is not enabled', async () => { + const askListResponses = [ + flowModule.DeploymentTypes.OTHER, + 'Yes', // AGENT_CORE_GATEWAY_MODE + 'No', // iamAuthEnabled + 'No', // enterMore? + ]; + td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); + + const askInputResponses = [ + 'us-east-1_999999999', + ]; + td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); + + const result = await flowModule.gatewayConnectivity(buildInstallConfig({ isDaEnabled: true, isTaEnabled: false })); + + expect(result.agentCoreGatewayMode).to.equal(true); + expect(result.agentCore.logGroupPrefix).to.equal(''); + expect(result.agentCore.cloudTrailEnabled).to.equal(false); + expect(result.agentCore.cloudTrailBucket).to.equal(''); + expect(td.explain(promptStubs.askInput).callCount).to.equal(1); + expect(td.explain(promptStubs.askList).callCount).to.equal(4); }); it('enables agentcore gateway mode and collects multiple cognito pools', async () => { const askListResponses = [ flowModule.DeploymentTypes.OTHER, - 'Yes', // APIGWCWRoleSetup - 'No', // fullTransactionLogging 'Yes', // AGENT_CORE_GATEWAY_MODE 'No', // iamAuthEnabled 'Yes', // enterMore? (add another pool) 'No', // enterMore? + 'No', // AGENTCORE_CLOUDTRAILENABLED ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); const askInputResponses = [ - 'agents-bucket', - '/aws/apigw/logs', - 'stage-tag', - '', 'us-east-1_111111111', 'eu-west-1_222222222', - '/aws/da/logs', - '/aws/ta/logs', + '', + 'my-bucket-2', ]; td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); @@ -291,8 +307,10 @@ describe('AWS on-prem agent flow', () => { expect(result.cognitoUserPoolIDs).to.have.length(2); expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_111111111'); expect(result.cognitoUserPoolIDs[1]).to.equal('eu-west-1_222222222'); - expect(td.explain(promptStubs.askInput).callCount).to.equal(8); - expect(td.explain(promptStubs.askList).callCount).to.equal(7); + expect(result.agentCore.cloudTrailEnabled).to.equal(false); + expect(result.agentCore.cloudTrailBucket).to.equal('my-bucket-2'); + expect(td.explain(promptStubs.askInput).callCount).to.equal(4); + expect(td.explain(promptStubs.askList).callCount).to.equal(6); }); it('stops question flow when AWS region lookup fails', async () => { @@ -429,7 +447,7 @@ function createHelpersStubs() { this.region = 'us-east-1'; this.stageTagName = ''; this.agentCoreGatewayMode = false; - this.agentCore = { logGroupPrefix: '', iamAuthEnabled: false }; + this.agentCore = { logGroupPrefix: '', iamAuthEnabled: false, cloudTrailEnabled: false, cloudTrailBucket: '' }; this.cognitoUserPoolIDs = []; this.cloudFormationConfig = { APIGWCWRoleSetup: '', @@ -461,9 +479,11 @@ function createHelpersStubs() { return { AWSAgentValues, AWSAgentCoreConfig: class AWSAgentCoreConfig { - constructor(logGroupPrefix, iamAuthEnabled) { + constructor(logGroupPrefix, iamAuthEnabled, cloudTrailEnabled, cloudTrailBucket) { this.logGroupPrefix = logGroupPrefix ?? ''; this.iamAuthEnabled = iamAuthEnabled ?? false; + this.cloudTrailEnabled = cloudTrailEnabled ?? false; + this.cloudTrailBucket = cloudTrailBucket ?? ''; } }, AWSRegexPatterns: { diff --git a/test/lib/engage/services/install-service/saas/test-aws.saas.js b/test/lib/engage/services/install-service/saas/test-aws.saas.js index 62f24653..b2685c7f 100644 --- a/test/lib/engage/services/install-service/saas/test-aws.saas.js +++ b/test/lib/engage/services/install-service/saas/test-aws.saas.js @@ -161,18 +161,20 @@ describe('AWS SaaS agent flow', () => { 'arn:aws:iam::000000000000:role/name-of-role', 'external-id', 'stage-tag', - '/aws/prefix', 'us-east-1_123456789', + '/aws/prefix', + 'my-cloudtrail-bucket', 'arn:aws:logs:us-east-1:000000000000:log-group:my-group', ]; td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); const askListResponses = [ 'Assume Role Policy', - engageTypes.YesNo.Yes, - engageTypes.YesNo.Yes, - engageTypes.YesNo.No, - engageTypes.YesNo.No, + engageTypes.YesNo.Yes, // AGENT_CORE_GATEWAY_MODE + engageTypes.YesNo.Yes, // AGENT_CORE_IAM_AUTH + engageTypes.YesNo.No, // ENTER_MORE_COGNITO_USER_POOL_IDS + engageTypes.YesNo.Yes, // AGENTCORE_CLOUDTRAILENABLED + engageTypes.YesNo.No, // FULL_TRANSACTION_LOGGING ]; td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); @@ -182,9 +184,37 @@ describe('AWS SaaS agent flow', () => { expect(result.agentCore.iamAuthEnabled).to.equal(true); expect(result.cognitoUserPoolIDs).to.have.length(1); expect(result.cognitoUserPoolIDs[0]).to.equal('us-east-1_123456789'); + expect(result.agentCore.cloudTrailEnabled).to.equal(true); + expect(result.agentCore.cloudTrailBucket).to.equal('my-cloudtrail-bucket'); expect(result.fullTransactionLogging).to.equal(false); }); + it('skips the log group prefix and CloudTrail prompts when TA is not enabled', async () => { + const askInputResponses = [ + 'arn:aws:iam::000000000000:role/name-of-role', + 'external-id', + 'stage-tag', + 'us-east-1_999999999', + ]; + td.when(promptStubs.askInput(td.matchers.anything())).thenDo(() => askInputResponses.shift()); + + const askListResponses = [ + 'Assume Role Policy', + engageTypes.YesNo.Yes, // AGENT_CORE_GATEWAY_MODE + engageTypes.YesNo.No, // AGENT_CORE_IAM_AUTH + engageTypes.YesNo.No, // ENTER_MORE_COGNITO_USER_POOL_IDS + ]; + td.when(promptStubs.askList(td.matchers.anything())).thenDo(() => askListResponses.shift()); + + const result = await flowModule.AWSSaaSInstallMethods.AskGatewayQuestions(buildInstallConfig(engageTypes.GatewayTypes.AWS_GATEWAY, false)); + expect(result.agentCoreGatewayMode).to.equal(true); + expect(result.agentCore.logGroupPrefix).to.be.undefined; + expect(result.agentCore.cloudTrailEnabled).to.be.undefined; + expect(result.agentCore.cloudTrailBucket).to.be.undefined; + expect(td.explain(promptStubs.askInput).callCount).to.equal(4); + expect(td.explain(promptStubs.askList).callCount).to.equal(4); + }); + it('builds AWS dataplane config when TA enabled', async () => { const installConfig = buildInstallConfig(engageTypes.GatewayTypes.AWS_GATEWAY, true); installConfig.gatewayConfig = { From 62601784bdb819e67f6d563dbac6baaf488e5f72 Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Thu, 30 Jul 2026 11:04:18 -0700 Subject: [PATCH 5/6] APIGOV-32905 Rename "Amazon API Gateway" to "AWS" --- src/commands/engage/install/agents.ts | 2 +- src/lib/engage/types.ts | 4 ++-- src/lib/engage/utils/agents/flows/awsAgents.ts | 4 ++-- src/lib/engage/utils/agents/flows/awsSaasAgents.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/commands/engage/install/agents.ts b/src/commands/engage/install/agents.ts index bfe67b36..fa188f29 100644 --- a/src/commands/engage/install/agents.ts +++ b/src/commands/engage/install/agents.ts @@ -5,7 +5,7 @@ import { installAgents } from '../../../lib/engage/services/install-service.js'; import { highlight } from '../../../lib/logger.js'; export class EngageInstallAgentsCommand extends Command { - static override summary = 'Amplify API Gateway / Apigee X Gateway / Amazon API Gateway / Azure API Gateway / Azure EventHub / Backstage / GitLab / Istio / Kafka /' + static override summary = 'Amplify API Gateway / Apigee X Gateway / AWS / Azure API Gateway / Azure EventHub / Backstage / GitLab / Istio / Kafka /' + ' Graylog / IBM API Connect / SwaggerHub / Software AG WebMethods / Traceable / SAP API Portal / Sensedia / WSO2'; static override aliases = [ 'central:install:agents' ]; diff --git a/src/lib/engage/types.ts b/src/lib/engage/types.ts index 4ec6192b..4ef2fb9a 100644 --- a/src/lib/engage/types.ts +++ b/src/lib/engage/types.ts @@ -806,7 +806,7 @@ export enum GatewayTypes { AKAMAI = 'Akamai', EDGE_GATEWAY = 'Amplify API Gateway', APIGEEX_GATEWAY = 'Apigee X Gateway', - AWS_GATEWAY = 'Amazon API Gateway', + AWS_GATEWAY = 'AWS', AZURE_GATEWAY = 'Azure API Gateway', AZURE_EVENTHUB = 'Azure EventHub', GITLAB = 'GitLab', @@ -825,7 +825,7 @@ export enum GatewayTypes { export enum SaaSGatewayTypes { AKAMAI = 'Akamai', - AWS_GATEWAY = 'Amazon API Gateway', + AWS_GATEWAY = 'AWS', APIGEEX_GATEWAY = 'Apigee X Gateway', GITHUB = 'GitHub', SWAGGERHUB = 'SwaggerHub', diff --git a/src/lib/engage/utils/agents/flows/awsAgents.ts b/src/lib/engage/utils/agents/flows/awsAgents.ts index 78d09265..b4e39353 100644 --- a/src/lib/engage/utils/agents/flows/awsAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsAgents.ts @@ -67,7 +67,7 @@ export const AWSPrompts = { PUBLIC_IP: 'Assign a Public IP Address to this, only change if your VPC has a NAT Gateway', SECURITY_GROUP: 'Enter the Security Group for the EC2 Instance of ECS Container', SETUP_APIGW_CW: - 'The Amazon API Gateway service requires a role to write usage logs to Cloud Watch. Do you want to configure that?', + 'The AWS API Gateway service requires a role to write usage logs to Cloud Watch. Do you want to configure that?', SSH_LOCATION: 'Enter the IP address range that can be used to SSH to the EC2 instances', SSM_PRIVATE: 'Enter the name of the SSM Parameter holding the Private Key', SSM_PUBLIC: 'Enter the name of the SSM Parameter holding the Public Key', @@ -249,7 +249,7 @@ async function configureECSDeployment(awsAgentValues: helpers.AWSAgentValues): P } export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Promise => { - installConfig.log('\nCONNECTION TO AMAZON API GATEWAY:'); + installConfig.log('\nCONNECTION TO AWS:'); installConfig.log( chalk.gray( 'You need credentials for executing the AWS CLI commands.\n' diff --git a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts index 2ca8e6e2..d0bd1440 100644 --- a/src/lib/engage/utils/agents/flows/awsSaasAgents.ts +++ b/src/lib/engage/utils/agents/flows/awsSaasAgents.ts @@ -231,10 +231,10 @@ const askForAgentCoreGatewayMode = async (agentValues: SaasAWSAgentValues, insta }; export const gatewayConnectivity = async (installConfig: AgentInstallConfig): Promise => { - installConfig.log('\nCONNECTION TO AMAZON API GATEWAY:'); + installConfig.log('\nCONNECTION TO AWS:'); installConfig.log( chalk.gray( - 'The Discovery Agent needs to connect to the AWS API Gateway to discover API\'s for publishing to Amplify Engage' + 'The Discovery Agent needs to connect to the AWS to discover API\'s for publishing to Amplify Engage' ) ); From 4f3967b624922a497652953951b5f61768bdbec5 Mon Sep 17 00:00:00 2001 From: Deepak Kasu Date: Mon, 3 Aug 2026 14:49:29 -0700 Subject: [PATCH 6/6] Fix lint --- src/lib/engage/services/create-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/engage/services/create-service.ts b/src/lib/engage/services/create-service.ts index a37474b1..c6380a08 100644 --- a/src/lib/engage/services/create-service.ts +++ b/src/lib/engage/services/create-service.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import { ApiServerClient } from '../clients-external/apiserverclient.js'; import { DefinitionsManager } from '../results/DefinitionsManager.js'; -import { AgentResourceCreateResult, AgentResourceKind, AgentTypes, ApiServerClientSingleResult, ApiServerVersions, BundleType, CreateCommandParams, CreateCommandResult, CreateEnvironmentCommandParams, DataPlaneNames, EngageCommandParams, GenericResource } from '../types.js'; +import { AgentResourceCreateResult, AgentResourceKind, AgentTypes, ApiServerClientSingleResult, BundleType, CreateCommandParams, CreateCommandResult, CreateEnvironmentCommandParams, DataPlaneNames, EngageCommandParams, GenericResource } from '../types.js'; import { getLatestServedAPIVersion, loadAndVerifySpecs, verifyFile } from '../utils/utils.js'; import { askInput, askList } from '../utils/basic-prompts.js'; import { askAgentName, askEnvironmentName } from '../utils/agents/inputs.js';