diff --git a/.gitignore b/.gitignore index 16d72972..601bba8d 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ deployment/global-s3-assets/ deployment/regional-s3-assets/ deployment/viperlight deployment/staging/ +deployment/open-source ### macOS ### # General diff --git a/CHANGELOG.md b/CHANGELOG.md index 93295973..5e128c93 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.4.4] - 2025-09-24 + +### Security + +- Updated axios from `1.7.7` to `1.12.1` to mitigate [CVE-2025-58754](https://avd.aquasec.com/nvd/cve-2025-58754), a DoS vulnerability. +- Updated Python Lambda base image from `public.ecr.aws/lambda/python:3.12.2025.09.02.19` to `public.ecr.aws/lambda/python:3.12.2025.09.22.12` to address [CVE-2025-24528](https://avd.aquasec.com/nvd/cve-2025-24528), [CVE-2025-3576](https://avd.aquasec.com/nvd/cve-2025-3576), [CVE-2025-7425](https://avd.aquasec.com/nvd/cve-2025-7425), and [CVE-2025-8058](https://avd.aquasec.com/nvd/cve-2025-8058). +- Removed deprecated NPM package "fs" has been identified as potentially vulnerable to package takeover. + +### Fixed + +- Fixed solution CloudFormation template deployment failures in AWS China partition by implementing partition-aware S3 URL generation [Issue #338](https://github.com/aws-solutions/centralized-logging-with-opensearch/issues/338) +- Fixed timeout issue with Get Agent status API by optimizing retry logic to work within AppSync's 30 second timeout limit + ## [2.4.3] - 2025-09-03 ### Security diff --git a/NOTICE.txt b/NOTICE.txt index 637647a8..dd886c3b 100755 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1777,6 +1777,18 @@ napi-postinstall under the MIT license. @unrs/resolver-binding-linux-x64-musl under the MIT license. exit-x under the MIT license. pygments under the 0BSD license. +set-proto under the MIT license. +side-channel-map under the MIT license. +safe-push-apply under the MIT license. +async-function under the MIT license. +side-channel-list under the MIT license. +call-bound under the MIT license. +side-channel-weakmap under the MIT license. +own-keys under the MIT license. +@babel/plugin-transform-explicit-resource-management under the MIT license. +wsl-utils under the MIT license. +baseline-browser-mapping under the Apache-2.0 license. +@babel/helper-globals under the Apache-2.0 license. ******************** OPEN SOURCE LICENSES diff --git a/deployment/cdk-solution-helper/package.json b/deployment/cdk-solution-helper/package.json index 54df0c3f..3764e3ea 100755 --- a/deployment/cdk-solution-helper/package.json +++ b/deployment/cdk-solution-helper/package.json @@ -7,11 +7,5 @@ "name": "Amazon Web Services", "url": "https://aws.amazon.com/solutions", "organization": true - }, - "devDependencies": { - "fs": "0.0.1-security" - }, - "dependencies": { - "fs": "0.0.1-security" } } diff --git a/deployment/ecr/clo-s3-list-objects/Dockerfile b/deployment/ecr/clo-s3-list-objects/Dockerfile index 5d8e7b44..6561b864 100644 --- a/deployment/ecr/clo-s3-list-objects/Dockerfile +++ b/deployment/ecr/clo-s3-list-objects/Dockerfile @@ -1,4 +1,4 @@ -FROM public.ecr.aws/lambda/python:3.12.2025.09.02.19 AS builder +FROM public.ecr.aws/lambda/python:3.12.2025.09.22.12 AS builder WORKDIR /build @@ -14,7 +14,7 @@ RUN python -m venv .venv && \ cd common-lib && \ poetry build -FROM public.ecr.aws/lambda/python:3.12.2025.09.02.19 +FROM public.ecr.aws/lambda/python:3.12.2025.09.22.12 WORKDIR /ws diff --git a/source/constructs/lambda/api/log_agent_status/lambda_function.py b/source/constructs/lambda/api/log_agent_status/lambda_function.py index a228fc91..d58effb5 100644 --- a/source/constructs/lambda/api/log_agent_status/lambda_function.py +++ b/source/constructs/lambda/api/log_agent_status/lambda_function.py @@ -190,22 +190,22 @@ def list_command_invocations(ssm_client, command_id, details=True, maxResults=50 def handle_command_invocations( - ssm_client, command_id, max_retries=10, retry_delay=0.25 + ssm_client, command_id, max_retries=13, retry_delay=2.0 ): """ Handles the list of command invocations for the specified AWS Systems Manager command. - If any of the invocations are in progress, it will retry the command until all invocations are completed or the maximum number of retries is reached. - + Uses fixed retry delay to stay within AppSync's 30-second timeout. + Args: ssm_client (boto3.client): An AWS Systems Manager client. command_id (str): The ID of the command to handle. - max_retries (int, optional): The maximum number of times to retry the command. Defaults to 5. - retry_delay (float, optional): The number of seconds to wait between retries. Defaults to 0.5. + max_retries (int, optional): Maximum retries (default: 13) + retry_delay (float, optional): Delay between retries (default: 2.0 seconds) Returns: list: The list of command invocations. """ - for _ in range(max_retries): + for attempt in range(max_retries): command_invocations = list(list_command_invocations(ssm_client, command_id)) in_progress_count = len( list( @@ -216,13 +216,14 @@ def handle_command_invocations( return command_invocations logger.info( - f"Retrying command {command_id} ({in_progress_count}/{len(command_invocations)} invocations in progress)" + f"Retrying command {command_id} ({in_progress_count}/{len(command_invocations)} invocations in progress) - attempt {attempt + 1}/{max_retries}" ) - time.sleep(retry_delay) - retry_delay *= 2 # Exponential backoff + + if attempt < max_retries - 1: + time.sleep(retry_delay) raise TimeoutError( - f"Command {command_id} did not complete within {sum(2 ** i * retry_delay for i in range(max_retries))} seconds." + f"Command {command_id} did not complete within {max_retries * retry_delay} seconds." ) diff --git a/source/constructs/lambda/custom-resource/lambda_function.py b/source/constructs/lambda/custom-resource/lambda_function.py index 4cf2a477..82fa0159 100644 --- a/source/constructs/lambda/custom-resource/lambda_function.py +++ b/source/constructs/lambda/custom-resource/lambda_function.py @@ -15,6 +15,7 @@ solution_version = os.environ.get("SOLUTION_VERSION") solution_name = os.environ.get("SOLUTION_NAME") template_bucket = os.environ.get("TEMPLATE_OUTPUT_BUCKET") +template_base_url = os.environ.get("TEMPLATE_BASE_URL") bucket_name = os.environ.get("WEB_BUCKET_NAME") api_endpoint = os.environ.get("API_ENDPOINT") user_pool_id = os.environ.get("USER_POOL_ID") @@ -121,6 +122,7 @@ def get_config_str(): "solution_name": solution_name, "sns_email_topic_arn": SNS_EMAIL_TOPIC_ARN, "template_bucket": template_bucket, + "template_base_url": template_base_url, } return json.dumps(export_json) diff --git a/source/constructs/lambda/main/cfnHelper/lambda_function.py b/source/constructs/lambda/main/cfnHelper/lambda_function.py index e0e236be..fd0363a6 100644 --- a/source/constructs/lambda/main/cfnHelper/lambda_function.py +++ b/source/constructs/lambda/main/cfnHelper/lambda_function.py @@ -20,9 +20,9 @@ default_region = os.environ.get("AWS_REGION") -template_output_bucket = os.environ.get("TEMPLATE_OUTPUT_BUCKET", "aws-gcr-solutions") +template_base_url = os.environ.get("TEMPLATE_BASE_URL") solution_name = os.environ.get("SOLUTION_NAME", "clo") -template_prefix = f"https://{template_output_bucket}.s3.amazonaws.com/{solution_name}/{solution_version}" +template_prefix = f"{template_base_url}/{solution_name}/{solution_version}" sts = boto3.client("sts", config=default_config) account_id = sts.get_caller_identity()["Account"] diff --git a/source/constructs/lambda/main/cfnHelper/test/conftest.py b/source/constructs/lambda/main/cfnHelper/test/conftest.py index b7569068..ddad105c 100644 --- a/source/constructs/lambda/main/cfnHelper/test/conftest.py +++ b/source/constructs/lambda/main/cfnHelper/test/conftest.py @@ -17,6 +17,6 @@ def default_environment_variables(): os.environ["SOLUTION_VERSION"] = "v1.0.0" os.environ["SOLUTION_ID"] = "SOXXXX" - os.environ["TEMPLATE_OUTPUT_BUCKET"] = "solution-bucket" + os.environ["TEMPLATE_BASE_URL"] = "https://solution-bucket.s3.amazonaws.com" os.environ["SUB_ACCOUNT_LINK_TABLE_NAME"] = "mocked-sub-account-link-table-name" os.environ["SUB_ACCOUNT_LINK_TABLE"] = "mocked-sub-account-link-table-name" diff --git a/source/constructs/lambda/main/cfnHelper/test/test_lambda_function.py b/source/constructs/lambda/main/cfnHelper/test/test_lambda_function.py index 17d8ef33..2d539f4d 100644 --- a/source/constructs/lambda/main/cfnHelper/test/test_lambda_function.py +++ b/source/constructs/lambda/main/cfnHelper/test/test_lambda_function.py @@ -42,13 +42,18 @@ def s3_client(): key = f"{solution_name}/{version}/AlarmForOpenSearch.template" s3 = boto3.resource("s3", region_name=region) - # Create the bucket - template_bucket = os.environ.get("TEMPLATE_OUTPUT_BUCKET") - s3.create_bucket(Bucket=template_bucket) + # Create the bucket - extract bucket name from TEMPLATE_BASE_URL + template_base_url = os.environ.get("TEMPLATE_BASE_URL") + if template_base_url: + bucket_name = template_base_url.replace("https://", "").split(".s3.")[0] + else: + bucket_name = "solution-bucket" + + s3.create_bucket(Bucket=bucket_name) # upload template file data = open("./test/template/test.template", "rb") - s3.Bucket(template_bucket).put_object(Key=key, Body=data) + s3.Bucket(bucket_name).put_object(Key=key, Body=data) yield diff --git a/source/constructs/lib/api/api-stack.ts b/source/constructs/lib/api/api-stack.ts index db13b8f3..cbe861af 100644 --- a/source/constructs/lib/api/api-stack.ts +++ b/source/constructs/lib/api/api-stack.ts @@ -114,6 +114,10 @@ export interface APIProps { readonly snsEmailTopic: sns.Topic; readonly sendAnonymizedUsageData: string; readonly solutionUuid: string; + + readonly s3Endpoint: string; + readonly templateBucketName: string; + readonly templateBaseUrl: string; } /** @@ -136,7 +140,7 @@ export class APIStack extends Construct { }); apiStack.graphqlApi - .addHttpDataSource('LatestVersionDS', 'https://s3.amazonaws.com') + .addHttpDataSource('LatestVersionDS', props.s3Endpoint) .createResolver('LatestVersionResolver', { typeName: 'Query', fieldName: 'latestVersion', @@ -144,7 +148,7 @@ export class APIStack extends Construct { JSON.stringify({ version: '2018-05-29', method: 'GET', - resourcePath: `/${TEMPLATE_OUTPUT_BUCKET}/centralized-logging-with-opensearch/latest/version`, + resourcePath: `/${props.templateBucketName}/centralized-logging-with-opensearch/latest/version`, params: { headers: { 'Content-Type': 'application/json', @@ -183,6 +187,7 @@ export class APIStack extends Construct { stackPrefix: props.stackPrefix, solutionId: props.solutionId, microBatchStack: props.microBatchStack, + templateBaseUrl: props.templateBaseUrl, }); NagSuppressions.addResourceSuppressions( cfnFlow, diff --git a/source/constructs/lib/main-stack.ts b/source/constructs/lib/main-stack.ts index 70322e44..46cde145 100644 --- a/source/constructs/lib/main-stack.ts +++ b/source/constructs/lib/main-stack.ts @@ -20,7 +20,7 @@ import { aws_s3 as s3, aws_sqs as sqs, Stack, - StackProps + StackProps, } from 'aws-cdk-lib'; import { Vpc } from 'aws-cdk-lib/aws-ec2'; import { NagSuppressions } from 'cdk-nag'; @@ -154,6 +154,34 @@ export class MainStack extends Stack { '' ); + // Create China partition condition and resolve template URLs + const isChinaCondition = new CfnCondition(this, 'IsChinaPartition', { + expression: Fn.conditionEquals(Aws.PARTITION, 'aws-cn'), + }); + + const templateBucketBase = + process.env.TEMPLATE_OUTPUT_BUCKET || 'aws-gcr-solutions'; + const globalTemplateBucket = templateBucketBase; + const chinaTemplateBucket = `${templateBucketBase}-cn`; + + const templateBaseUrl = Fn.conditionIf( + isChinaCondition.logicalId, + `https://${chinaTemplateBucket}.s3.cn-north-1.amazonaws.com.cn`, + `https://${globalTemplateBucket}.s3.amazonaws.com` + ).toString(); + + const s3Endpoint = Fn.conditionIf( + isChinaCondition.logicalId, + 'https://s3.cn-north-1.amazonaws.com.cn', + 'https://s3.amazonaws.com' + ).toString(); + + const templateBucketName = Fn.conditionIf( + isChinaCondition.logicalId, + chinaTemplateBucket, + globalTemplateBucket + ).toString(); + if (this.authType === AuthType.OIDC) { oidcProvider = new CfnParameter(this, 'OidcProvider', { type: 'String', @@ -633,6 +661,9 @@ export class MainStack extends Stack { 'SendAnonymizedUsageData', 'Data' ), + s3Endpoint: s3Endpoint, + templateBucketName: templateBucketName, + templateBaseUrl: templateBaseUrl, }); NagSuppressions.addResourceSuppressions( apiStack, @@ -709,6 +740,8 @@ export class MainStack extends Stack { authenticationType: this.authType, webUILoggingBucket: portalStack.webUILoggingBucket.bucketName, snsEmailTopic: microBatchStack.microBatchSNSStack.SNSSendEmailTopic, + templateBucketName: templateBucketName, + templateBaseUrl: templateBaseUrl, }); // Allow init config function to put aws-exports.json to portal bucket @@ -732,10 +765,16 @@ export class MainStack extends Stack { value: portalStack.portalUrl, }).overrideLogicalId('WebConsoleUrl'); - Aspects.of(this).add(new CfnGuardSuppressResourceList({ - "AWS::IAM::Role": ["IAM_NO_INLINE_POLICY_CHECK", "IAM_POLICYDOCUMENT_NO_WILDCARD_RESOURCE", "CFN_NO_EXPLICIT_RESOURCE_NAMES"], // Explicit role names required for cross account assumption - "AWS::Logs::LogGroup": ["CLOUDWATCH_LOG_GROUP_ENCRYPTED"], // Using service default encryption https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/data-protection.html - })) + Aspects.of(this).add( + new CfnGuardSuppressResourceList({ + 'AWS::IAM::Role': [ + 'IAM_NO_INLINE_POLICY_CHECK', + 'IAM_POLICYDOCUMENT_NO_WILDCARD_RESOURCE', + 'CFN_NO_EXPLICIT_RESOURCE_NAMES', + ], // Explicit role names required for cross account assumption + 'AWS::Logs::LogGroup': ['CLOUDWATCH_LOG_GROUP_ENCRYPTED'], // Using service default encryption https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/data-protection.html + }) + ); } private addToParamGroups(label: string, ...param: string[]) { @@ -749,7 +788,7 @@ export class MainStack extends Stack { this.paramLabels[param] = { default: label, }; - } + } } class AddS3BucketNotificationsDependency implements IAspect { diff --git a/source/constructs/lib/main/cfn-flow-stack.ts b/source/constructs/lib/main/cfn-flow-stack.ts index 1dbb1997..3b407849 100644 --- a/source/constructs/lib/main/cfn-flow-stack.ts +++ b/source/constructs/lib/main/cfn-flow-stack.ts @@ -37,6 +37,8 @@ export interface CfnFlowProps { readonly solutionId: string; readonly microBatchStack: MicroBatchStack; + + readonly templateBaseUrl: string; } /** @@ -52,8 +54,6 @@ export class CfnFlowStack extends Construct { const stackArn = `arn:${Aws.PARTITION}:cloudformation:${Aws.REGION}:${Aws.ACCOUNT_ID}:stack/${props.stackPrefix}*`; - const templateBucket = - process.env.TEMPLATE_OUTPUT_BUCKET || 'aws-gcr-solutions'; const solutionName = process.env.SOLUTION_TRADEMARKEDNAME || 'log-hub'; // Old name // Create a Lambda to handle all the cloudformation related tasks. @@ -67,7 +67,7 @@ export class CfnFlowStack extends Construct { memorySize: 128, layers: [SharedPythonLayer.getInstance(this)], environment: { - TEMPLATE_OUTPUT_BUCKET: templateBucket, + TEMPLATE_BASE_URL: props.templateBaseUrl, SOLUTION_NAME: solutionName, SOLUTION_ID: props.solutionId, SUB_ACCOUNT_LINK_TABLE_NAME: props.subAccountLinkTable.tableName, diff --git a/source/constructs/lib/main/cr-stack.ts b/source/constructs/lib/main/cr-stack.ts index 6197715e..a46b794e 100644 --- a/source/constructs/lib/main/cr-stack.ts +++ b/source/constructs/lib/main/cr-stack.ts @@ -3,17 +3,17 @@ import * as path from 'path'; import { + Aspects, Aws, - Duration, CfnCondition, - Fn, - Aspects, - IAspect, CfnResource, + custom_resources as cr, CustomResource, + Duration, + Fn, aws_iam as iam, + IAspect, aws_lambda as lambda, - custom_resources as cr, aws_sns as sns, } from 'aws-cdk-lib'; import { Construct, IConstruct } from 'constructs'; @@ -106,6 +106,9 @@ export interface CRProps { readonly apiStack: APIStack; readonly webUILoggingBucket: string; readonly snsEmailTopic: sns.Topic; + + readonly templateBucketName: string; + readonly templateBaseUrl: string; } /** @@ -117,8 +120,6 @@ export class CustomResourceStack extends Construct { constructor(scope: Construct, id: string, props: CRProps) { super(scope, id); - const templateBucket = - process.env.TEMPLATE_OUTPUT_BUCKET || 'aws-gcr-solutions'; const solutionName = process.env.SOLUTION_TRADEMARKEDNAME || 'log-hub'; // Old name // If in China Region, disable install latest aws-sdk @@ -163,7 +164,8 @@ export class CustomResourceStack extends Construct { STAGING_BUCKET: props.stagingBucket, DEFAULT_CMK_ARN: props.cmkKeyArn, SOLUTION_VERSION: process.env.VERSION || 'v1.0.0', - TEMPLATE_OUTPUT_BUCKET: templateBucket, + TEMPLATE_OUTPUT_BUCKET: props.templateBucketName, + TEMPLATE_BASE_URL: props.templateBaseUrl, SOLUTION_NAME: solutionName, ACCESS_LOGGING_BUCKET: props.webUILoggingBucket, SNS_EMAIL_TOPIC_ARN: props.snsEmailTopic.topicArn, @@ -203,10 +205,7 @@ export class CustomResourceStack extends Construct { this.initConfigFn.addToRolePolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, - actions: [ - 'kms:DescribeCustomKeyStores', - 'kms:DescribeKey', - ], + actions: ['kms:DescribeCustomKeyStores', 'kms:DescribeKey'], resources: ['*'], }) ); @@ -236,7 +235,7 @@ export class CustomResourceStack extends Construct { } class InjectCustomerResourceConfig implements IAspect { - public constructor(private installLatestAwsSdk: string) { } + public constructor(private installLatestAwsSdk: string) {} public visit(node: IConstruct): void { if (node instanceof CfnResource && node.cfnResourceType === 'Custom::AWS') { diff --git a/source/constructs/package.json b/source/constructs/package.json index 594a4677..cf4c4372 100644 --- a/source/constructs/package.json +++ b/source/constructs/package.json @@ -1,7 +1,7 @@ { "name": "centralized-logging-with-opensearch", "description": "Centralized logging with opensearch (SO8025)", - "version": "2.4.3", + "version": "2.4.4", "license": "Apache-2.0", "author": { "name": "Amazon Web Services", diff --git a/source/constructs/test/main.test.ts b/source/constructs/test/main.test.ts index 93f9f13b..112ee1a8 100644 --- a/source/constructs/test/main.test.ts +++ b/source/constructs/test/main.test.ts @@ -59,7 +59,13 @@ describe('MainStack', () => { template.hasResourceProperties('AWS::Lambda::Function', { Environment: { Variables: { - TEMPLATE_OUTPUT_BUCKET: 'aws-gcr-solutions', + TEMPLATE_BASE_URL: { + 'Fn::If': [ + 'IsChinaPartition', + 'https://aws-gcr-solutions-cn.s3.cn-north-1.amazonaws.com.cn', + 'https://aws-gcr-solutions.s3.amazonaws.com', + ], + }, SOLUTION_ID: 'SOXXXX', SOLUTION_VERSION: 'v1.0.0', }, @@ -134,7 +140,13 @@ describe('MainStack', () => { template.hasResourceProperties('AWS::Lambda::Function', { Environment: { Variables: { - TEMPLATE_OUTPUT_BUCKET: 'test-bucket', + TEMPLATE_BASE_URL: { + 'Fn::If': [ + 'IsChinaPartition', + 'https://test-bucket-cn.s3.cn-north-1.amazonaws.com.cn', + 'https://test-bucket.s3.amazonaws.com', + ], + }, SOLUTION_ID: 'SOXXXX', SOLUTION_VERSION: 'vX.Y.Z', }, diff --git a/source/portal/package.json b/source/portal/package.json index 54eb37fb..6af87843 100644 --- a/source/portal/package.json +++ b/source/portal/package.json @@ -24,7 +24,7 @@ "aws-amplify": "^5.3.12", "aws-appsync-auth-link": "^3.0.7", "aws-appsync-subscription-link": "^3.1.2", - "axios": "^1.8.2", + "axios": "^1.12.1", "classnames": "^2.3.2", "date-fns": "^2.30.0", "graphql-tag": "^2.12.6", @@ -141,9 +141,7 @@ "webpack-dev-server": "5.2.1", "brace-expansion": "2.0.2", "form-data": "3.0.4", - "axios": { - "form-data": "4.0.4" - }, + "axios": "^1.12.1", "on-headers": "1.1.0" } } \ No newline at end of file diff --git a/source/portal/src/assets/js/test/utils.test.ts b/source/portal/src/assets/js/test/utils.test.ts index 5025e5ed..606b614c 100644 --- a/source/portal/src/assets/js/test/utils.test.ts +++ b/source/portal/src/assets/js/test/utils.test.ts @@ -948,35 +948,31 @@ describe("buildASGLink", () => { }); describe("buildCrossAccountTemplateLink", () => { - it("should build the cross account template link for standard AWS regions", () => { - const region = "us-example-1"; + it("should build the cross account template link with template base URL", () => { const solutionVersion = "v1.0.0"; - const templateBucket = "myTemplateBucket"; const solutionName = "solutionName"; + const templateBaseUrl = "https://s3.amazonaws.com/solutions-reference"; const link = buildCrossAccountTemplateLink( - region, solutionVersion, - templateBucket, - solutionName + solutionName, + templateBaseUrl ); expect(link).toBe( - `https://${templateBucket}.s3.amazonaws.com/${solutionName}/${solutionVersion}/CrossAccount.template` + `${templateBaseUrl}/${solutionName}/${solutionVersion}/CrossAccount.template` ); }); it("should build the cross account template link for China regions", () => { - const region = "cn-example-1"; const solutionVersion = "v1.0.1"; - const templateBucket = "myTemplateBucket"; const solutionName = "myTemplate"; + const templateBaseUrl = "https://solutions-reference-cn.s3.cn-north-1.amazonaws.com.cn"; const link = buildCrossAccountTemplateLink( - region, solutionVersion, - templateBucket, - solutionName + solutionName, + templateBaseUrl ); expect(link).toBe( - `https://${templateBucket}.s3.amazonaws.com/${solutionName}/${solutionVersion}/CrossAccount.template` + `${templateBaseUrl}/${solutionName}/${solutionVersion}/CrossAccount.template` ); }); }); diff --git a/source/portal/src/assets/js/utils.ts b/source/portal/src/assets/js/utils.ts index 9e53cb50..5a7b5e83 100644 --- a/source/portal/src/assets/js/utils.ts +++ b/source/portal/src/assets/js/utils.ts @@ -1,11 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { AppPipeline, EngineType, SchedulerType, SubAccountLink } from "API"; import { format, parseISO } from "date-fns"; import { formatWithOptions } from "date-fns/fp"; -import { AppPipeline, EngineType, SchedulerType, SubAccountLink } from "API"; import i18n from "i18n"; -import { LogProcessorType, SelectProcessorType } from "reducer/selectProcessor"; import { ServiceLogDetailProps } from "pages/dataInjection/serviceLog/ServiceLogDetail"; +import { LogProcessorType, SelectProcessorType } from "reducer/selectProcessor"; const stackPrefix = "CL"; @@ -448,12 +448,11 @@ export const buildASGLink = (region: string, groupName: string): string => { }; export const buildCrossAccountTemplateLink = ( - region: string, solutionVersion: string, - templateBucket: string, - solutionName: string + solutionName: string, + templateBaseUrl: string ): string => { - return `https://${templateBucket}.s3.amazonaws.com/${solutionName}/${solutionVersion}/CrossAccount.template`; + return `${templateBaseUrl}/${solutionName}/${solutionVersion}/CrossAccount.template`; }; export const buildSQSLink = (region: string, queueName: string): string => { diff --git a/source/portal/src/pages/dataInjection/applicationLog/create/ec2/test/ChooseBufferLayer.test.tsx b/source/portal/src/pages/dataInjection/applicationLog/create/ec2/test/ChooseBufferLayer.test.tsx index 9d35ed29..f02bc469 100644 --- a/source/portal/src/pages/dataInjection/applicationLog/create/ec2/test/ChooseBufferLayer.test.tsx +++ b/source/portal/src/pages/dataInjection/applicationLog/create/ec2/test/ChooseBufferLayer.test.tsx @@ -63,7 +63,7 @@ describe("ChooseBufferLayer", () => { default_cmk_arn: "", solution_version: "", solution_name: "", - template_bucket: "", + template_base_url: "", sns_email_topic_arn: "", oidc_logout_url: "", })} diff --git a/source/portal/src/pages/resources/crossAccount/CrossAccountDetail.tsx b/source/portal/src/pages/resources/crossAccount/CrossAccountDetail.tsx index 82ea3ea0..e72f0714 100644 --- a/source/portal/src/pages/resources/crossAccount/CrossAccountDetail.tsx +++ b/source/portal/src/pages/resources/crossAccount/CrossAccountDetail.tsx @@ -138,10 +138,9 @@ const CrossAccountDetail: React.FC = () => {
{""} @@ -149,10 +148,9 @@ const CrossAccountDetail: React.FC = () => {
                         
                           {buildCrossAccountTemplateLink(
-                            amplifyConfig.aws_appsync_region,
                             amplifyConfig.solution_version,
-                            amplifyConfig.template_bucket,
-                            amplifyConfig.solution_name
+                            amplifyConfig.solution_name,
+                            amplifyConfig.template_base_url
                           )}
                         
                       
diff --git a/source/portal/src/pages/resources/crossAccount/LinkAnAccount.tsx b/source/portal/src/pages/resources/crossAccount/LinkAnAccount.tsx index e4af141a..c2ba607c 100644 --- a/source/portal/src/pages/resources/crossAccount/LinkAnAccount.tsx +++ b/source/portal/src/pages/resources/crossAccount/LinkAnAccount.tsx @@ -106,10 +106,9 @@ const LinkAnAccount: React.FC = () => {
{""} @@ -117,10 +116,9 @@ const LinkAnAccount: React.FC = () => {
                   
                     {buildCrossAccountTemplateLink(
-                      amplifyConfig.aws_appsync_region,
                       amplifyConfig.solution_version,
-                      amplifyConfig.template_bucket,
-                      amplifyConfig.solution_name
+                      amplifyConfig.solution_name,
+                      amplifyConfig.template_base_url
                     )}
                   
                 
diff --git a/source/portal/src/test/store.mock.ts b/source/portal/src/test/store.mock.ts index 80edada4..a43b7c69 100644 --- a/source/portal/src/test/store.mock.ts +++ b/source/portal/src/test/store.mock.ts @@ -22,7 +22,7 @@ export const AppStoreMockData = { default_cmk_arn: "arn:aws:kms:us-east-2:xxxx:key/xxx-xxx-xxx-xxx-xxx", solution_version: "develop", solution_name: "centralized-logging-with-opensearch", - template_bucket: "xxx-xxx-xxx", + template_base_url: "https://s3.amazonaws.com/xxx-xxx-xxx", }, openSideMenu: true, showInfoBar: false, diff --git a/source/portal/src/types/index.ts b/source/portal/src/types/index.ts index 47add50e..92f4bf92 100644 --- a/source/portal/src/types/index.ts +++ b/source/portal/src/types/index.ts @@ -54,7 +54,7 @@ export interface AmplifyConfigType { default_cmk_arn: string; solution_version: string; solution_name: string; - template_bucket: string; + template_base_url: string; sns_email_topic_arn: string; oidc_logout_url: string; }