diff --git a/packages/@aws-cdk/aws-codebuild/lib/linux-gpu-build-image.ts b/packages/@aws-cdk/aws-codebuild/lib/linux-gpu-build-image.ts index b26611745afbb..2a836fae59b69 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/linux-gpu-build-image.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/linux-gpu-build-image.ts @@ -1,6 +1,6 @@ import * as ecr from '@aws-cdk/aws-ecr'; import * as core from '@aws-cdk/core'; -import { FactName, RegionInfo } from '@aws-cdk/region-info'; +import { FactName } from '@aws-cdk/region-info'; import { BuildSpec } from './build-spec'; import { runScriptLinuxBuildSpec } from './private/run-script-linux-build-spec'; import { @@ -12,8 +12,6 @@ import { // eslint-disable-next-line no-duplicate-imports, import/order import { Construct } from '@aws-cdk/core'; -const mappingName = 'AwsDeepLearningContainersRepositoriesAccounts'; - /** * A CodeBuild GPU image running Linux. * @@ -109,38 +107,38 @@ export class LinuxGpuBuildImage implements IBindableBuildImage { public readonly type = 'LINUX_GPU_CONTAINER'; public readonly defaultComputeType = ComputeType.LARGE; - public readonly imageId: string; public readonly imagePullPrincipalType?: ImagePullPrincipalType = ImagePullPrincipalType.SERVICE_ROLE; + public readonly imageId: string; - private readonly accountExpression: string; + private _imageAccount?: string; private constructor(private readonly repositoryName: string, tag: string, private readonly account: string | undefined) { - this.accountExpression = account ?? core.Fn.findInMap(mappingName, core.Aws.REGION, 'repositoryAccount'); - this.imageId = `${this.accountExpression}.dkr.ecr.${core.Aws.REGION}.${core.Aws.URL_SUFFIX}/${repositoryName}:${tag}`; + const imageAccount = account ?? core.Lazy.string({ + produce: () => { + if (this._imageAccount === undefined) { + throw new Error('Make sure this \'LinuxGpuBuildImage\' is used in a CodeBuild Project construct'); + } + return this._imageAccount; + }, + }); + + // The value of imageId below *should* have been `Lazy.stringValue(() => repository.repositoryUriForTag(this.tag))`, + // but we can't change that anymore because someone somewhere might at this point have written code + // to do `image.imageId.includes('pytorch')` and changing this to a full-on token would break them. + this.imageId = `${imageAccount}.dkr.ecr.${core.Aws.REGION}.${core.Aws.URL_SUFFIX}/${repositoryName}:${tag}`; } public bind(scope: Construct, project: IProject, _options: BuildImageBindOptions): BuildImageConfig { - if (!this.account) { - const scopeStack = core.Stack.of(scope); - // Unfortunately, the account IDs of the DLC repositories are not the same in all regions. - // Because of that, use a (singleton) Mapping to find the correct account - if (!scopeStack.node.tryFindChild(mappingName)) { - const mapping: { [k1: string]: { [k2: string]: any } } = {}; - // get the accounts from the region-info module - const region2Accounts = RegionInfo.regionMap(FactName.DLC_REPOSITORY_ACCOUNT); - for (const [region, account] of Object.entries(region2Accounts)) { - mapping[region] = { repositoryAccount: account }; - } - new core.CfnMapping(scopeStack, mappingName, { mapping }); - } - } - + const account = this.account ?? core.Stack.of(scope).regionalFact(FactName.DLC_REPOSITORY_ACCOUNT); const repository = ecr.Repository.fromRepositoryAttributes(scope, 'AwsDlcRepositoryCodeBuild', { repositoryName: this.repositoryName, - repositoryArn: ecr.Repository.arnForLocalRepository(this.repositoryName, scope, this.accountExpression), + repositoryArn: ecr.Repository.arnForLocalRepository(this.repositoryName, scope, account), }); + repository.grantPull(project); + this._imageAccount = account; + return { }; } diff --git a/packages/@aws-cdk/aws-codebuild/lib/project.ts b/packages/@aws-cdk/aws-codebuild/lib/project.ts index f7a503d335cbc..c6a816e3aedd2 100644 --- a/packages/@aws-cdk/aws-codebuild/lib/project.ts +++ b/packages/@aws-cdk/aws-codebuild/lib/project.ts @@ -1138,9 +1138,8 @@ export class Project extends ProjectBase { } // bind - const bindFunction = (this.buildImage as any).bind; - if (bindFunction) { - bindFunction.call(this.buildImage, this, this, {}); + if (isBindableBuildImage(this.buildImage)) { + this.buildImage.bind(this, this, {}); } } @@ -2123,3 +2122,7 @@ export enum ProjectNotificationEvents { */ BUILD_PHASE_SUCCEEDED = 'codebuild-project-build-phase-success', } + +function isBindableBuildImage(x: unknown): x is IBindableBuildImage { + return typeof x === 'object' && !!x && !!(x as any).bind; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-codebuild/test/integ.aws-deep-learning-container-build-image.expected.json b/packages/@aws-cdk/aws-codebuild/test/integ.aws-deep-learning-container-build-image.expected.json index 3551f01dadd54..cee41b06e1ff9 100644 --- a/packages/@aws-cdk/aws-codebuild/test/integ.aws-deep-learning-container-build-image.expected.json +++ b/packages/@aws-cdk/aws-codebuild/test/integ.aws-deep-learning-container-build-image.expected.json @@ -135,11 +135,11 @@ ":", { "Fn::FindInMap": [ - "AwsDeepLearningContainersRepositoriesAccounts", + "DlcRepositoryAccountMap", { "Ref": "AWS::Region" }, - "repositoryAccount" + "value" ] }, ":repository/mxnet-training" @@ -177,11 +177,11 @@ [ { "Fn::FindInMap": [ - "AwsDeepLearningContainersRepositoriesAccounts", + "DlcRepositoryAccountMap", { "Ref": "AWS::Region" }, - "repositoryAccount" + "value" ] }, ".dkr.ecr.", @@ -215,66 +215,66 @@ } }, "Mappings": { - "AwsDeepLearningContainersRepositoriesAccounts": { + "DlcRepositoryAccountMap": { "ap-east-1": { - "repositoryAccount": "871362719292" + "value": "871362719292" }, "ap-northeast-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "ap-northeast-2": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "ap-south-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "ap-southeast-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "ap-southeast-2": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "ca-central-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "cn-north-1": { - "repositoryAccount": "727897471807" + "value": "727897471807" }, "cn-northwest-1": { - "repositoryAccount": "727897471807" + "value": "727897471807" }, "eu-central-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "eu-north-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "eu-west-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "eu-west-2": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "eu-west-3": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "me-south-1": { - "repositoryAccount": "217643126080" + "value": "217643126080" }, "sa-east-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "us-east-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "us-east-2": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "us-west-1": { - "repositoryAccount": "763104351884" + "value": "763104351884" }, "us-west-2": { - "repositoryAccount": "763104351884" + "value": "763104351884" } } } diff --git a/packages/@aws-cdk/aws-iam/lib/principals.ts b/packages/@aws-cdk/aws-iam/lib/principals.ts index 001792cbcc475..5a3abd6c0a76d 100644 --- a/packages/@aws-cdk/aws-iam/lib/principals.ts +++ b/packages/@aws-cdk/aws-iam/lib/principals.ts @@ -1,5 +1,5 @@ import * as cdk from '@aws-cdk/core'; -import { Default, RegionInfo } from '@aws-cdk/region-info'; +import { Default, FactName, RegionInfo } from '@aws-cdk/region-info'; import { IOpenIdConnectProvider } from './oidc-provider'; import { Condition, Conditions, PolicyStatement } from './policy-statement'; import { ISamlProvider } from './saml-provider'; @@ -331,6 +331,7 @@ export interface ServicePrincipalOpts { * The region in which the service is operating. * * @default the current Stack's region. + * @deprecated You should not need to set this. The stack's region is always correct. */ readonly region?: string; @@ -694,9 +695,23 @@ class ServicePrincipalToken implements cdk.IResolvable { } public resolve(ctx: cdk.IResolveContext) { - const region = this.opts.region || cdk.Stack.of(ctx.scope).region; - const fact = RegionInfo.get(region).servicePrincipal(this.service); - return fact || Default.servicePrincipal(this.service, region, cdk.Aws.URL_SUFFIX); + if (this.opts.region) { + // Special case, handle it separately to not break legacy behavior. + return ( + RegionInfo.get(this.opts.region).servicePrincipal(this.service) ?? + Default.servicePrincipal( + this.service, + this.opts.region, + cdk.Aws.URL_SUFFIX, + ) + ); + } + + const stack = cdk.Stack.of(ctx.scope); + return stack.regionalFact( + FactName.servicePrincipal(this.service), + Default.servicePrincipal(this.service, stack.region, cdk.Aws.URL_SUFFIX), + ); } public toString() { diff --git a/packages/@aws-cdk/aws-iam/lib/util.ts b/packages/@aws-cdk/aws-iam/lib/util.ts index 11ef02a44ff5c..831f625a1fdcf 100644 --- a/packages/@aws-cdk/aws-iam/lib/util.ts +++ b/packages/@aws-cdk/aws-iam/lib/util.ts @@ -137,4 +137,4 @@ export class UniqueStringSet implements IResolvable, IPostProcessor { function isEmptyObject(x: { [key: string]: any }): boolean { return Object.keys(x).length === 0; -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-iam/package.json b/packages/@aws-cdk/aws-iam/package.json index f3f4ab6f37d3c..5131d1c23670e 100644 --- a/packages/@aws-cdk/aws-iam/package.json +++ b/packages/@aws-cdk/aws-iam/package.json @@ -80,6 +80,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert-internal": "0.0.0", + "@aws-cdk/assertions": "0.0.0", "@aws-cdk/cdk-build-tools": "0.0.0", "@aws-cdk/cdk-integ-tools": "0.0.0", "@aws-cdk/cfn2ts": "0.0.0", diff --git a/packages/@aws-cdk/aws-iam/test/policy-document.test.ts b/packages/@aws-cdk/aws-iam/test/policy-document.test.ts index bf93e31901c6c..ff30531f5fc59 100644 --- a/packages/@aws-cdk/aws-iam/test/policy-document.test.ts +++ b/packages/@aws-cdk/aws-iam/test/policy-document.test.ts @@ -1,4 +1,5 @@ import '@aws-cdk/assert-internal/jest'; +import { testDeprecated } from '@aws-cdk/cdk-build-tools'; import { Lazy, Stack, Token } from '@aws-cdk/core'; import { AccountPrincipal, Anyone, AnyPrincipal, ArnPrincipal, CanonicalUserPrincipal, CompositePrincipal, @@ -444,7 +445,8 @@ describe('IAM policy document', () => { }); }); - test('regional service principals resolve appropriately (with user-set region)', () => { + // Deprecated: 'region' parameter to ServicePrincipal shouldn't be used. + testDeprecated('regional service principals resolve appropriately (with user-set region)', () => { const stack = new Stack(undefined, undefined, { env: { region: 'cn-northeast-1' } }); const s = new PolicyStatement(); s.addActions('test:Action'); diff --git a/packages/@aws-cdk/aws-iam/test/principals.test.ts b/packages/@aws-cdk/aws-iam/test/principals.test.ts index 1bf7d47950875..18dc72f4d760b 100644 --- a/packages/@aws-cdk/aws-iam/test/principals.test.ts +++ b/packages/@aws-cdk/aws-iam/test/principals.test.ts @@ -1,4 +1,5 @@ import '@aws-cdk/assert-internal/jest'; +import { Template } from '@aws-cdk/assertions'; import { App, CfnOutput, Stack } from '@aws-cdk/core'; import * as iam from '../lib'; @@ -243,4 +244,20 @@ test('PrincipalWithConditions inherits principalAccount from AccountPrincipal ', // THEN expect(accountPrincipal.principalAccount).toStrictEqual('123456789012'); expect(principalWithConditions.principalAccount).toStrictEqual('123456789012'); +}); + +test('ServicePrincipal in agnostic stack generates lookup table', () => { + // GIVEN + const stack = new Stack(); + + // WHEN + new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('ssm.amazonaws.com'), + }); + + // THEN + const template = Template.fromStack(stack); + const mappings = template.findMappings('ServiceprincipalMap'); + expect(mappings.ServiceprincipalMap['af-south-1']?.ssm).toEqual('ssm.af-south-1.amazonaws.com'); + expect(mappings.ServiceprincipalMap['us-east-1']?.ssm).toEqual('ssm.amazonaws.com'); }); \ No newline at end of file diff --git a/packages/@aws-cdk/aws-lambda/lib/lambda-insights.ts b/packages/@aws-cdk/aws-lambda/lib/lambda-insights.ts index c4dacfbde0149..4d1eee91cf10a 100644 --- a/packages/@aws-cdk/aws-lambda/lib/lambda-insights.ts +++ b/packages/@aws-cdk/aws-lambda/lib/lambda-insights.ts @@ -1,13 +1,10 @@ -import { Aws, CfnMapping, Fn, IResolveContext, Lazy, Stack, Token } from '@aws-cdk/core'; +import { Lazy, Stack, Token } from '@aws-cdk/core'; import { FactName, RegionInfo } from '@aws-cdk/region-info'; -import { Construct } from 'constructs'; +import { Construct, IConstruct } from 'constructs'; import { Architecture } from './architecture'; import { IFunction } from './function-base'; -// This is the name of the mapping that will be added to the CloudFormation template, if a stack is region agnostic -const DEFAULT_MAPPING_PREFIX = 'LambdaInsightsVersions'; - /** * Config returned from {@link LambdaInsightsVersion._bind} */ @@ -71,7 +68,7 @@ export abstract class LambdaInsightsVersion { class InsightsVersion extends LambdaInsightsVersion { public readonly layerVersionArn = Lazy.uncachedString({ - produce: (context) => getVersionArn(context, insightsVersion), + produce: (context) => getVersionArn(context.scope, insightsVersion), }); public _bind(_scope: Construct, _function: IFunction): InsightsBindConfig { @@ -83,9 +80,7 @@ export abstract class LambdaInsightsVersion { throw new Error(`Insights version ${insightsVersion} does not exist.`); } return { - arn: Lazy.uncachedString({ - produce: (context) => getVersionArn(context, insightsVersion, arch), - }), + arn: getVersionArn(_scope, insightsVersion, arch), }; } } @@ -111,9 +106,9 @@ export abstract class LambdaInsightsVersion { * * This function is run on CDK synthesis. */ -function getVersionArn(context: IResolveContext, insightsVersion: string, architecture?: string): string { +function getVersionArn(scope: IConstruct, insightsVersion: string, architecture?: string): string { - const scopeStack = Stack.of(context.scope); + const scopeStack = Stack.of(scope); const region = scopeStack.region; const arch = architecture ?? Architecture.X86_64.name; @@ -127,61 +122,5 @@ function getVersionArn(context: IResolveContext, insightsVersion: string, archit } // Otherwise, need to add a mapping to be looked up at deployment time - - /** - * See this for the context as to why the mappings are the way they are - * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html - * - * Mappings have to have a structure like this, and no functions can be used inside them: - * - * - - * -- { : "value1"}, - * -- { : "value2"} - * - * So we cannot have an otherwise ideal mapping like this, because '1.0.98.0' is non-alphanumeric: - * LambdaInsightsVersions - * - us-east-1 - * -- {'1.0.98.0': 'arn1'}, - * -- {'1.0.89.0': 'arn2'} - * - * To get around this limitation, this is the mapping structure: - * LambdaInsightsVersions10980 // for version 1.0.98.0 - * - us-east-1 - * -- {'arn': 'arn1'}, - * - us-east-2 - * -- {'arn': 'arn2'} - * LambdaInsightsVersions10890 // a separate mapping version 1.0.89.0 - * - us-east-1 - * -- {'arn': 'arn3'}, - * - us-east-2 - * -- {'arn': 'arn4'} - * LambdaInsightsVersions101190arm64 // a separate mapping version 1.0.119.0 arm64 - * - us-east-1 - * -- {'arn': 'arn3'}, - * - us-east-2 - * -- {'arn': 'arn4'} - */ - - let mapName = DEFAULT_MAPPING_PREFIX + insightsVersion.split('.').join(''); - // if the architecture is arm64 then append that to the end of the name - // this is so that we can have a separate mapping for x86 vs arm in scenarios - // where we have Lambda functions with both architectures in the same stack - if (arch === Architecture.ARM_64.name) { - mapName += arch; - } - const mapping: { [k1: string]: { [k2: string]: any } } = {}; - const region2arns = RegionInfo.regionMap(FactName.cloudwatchLambdaInsightsVersion(insightsVersion, arch)); - for (const [reg, arn] of Object.entries(region2arns)) { - mapping[reg] = { arn }; - } - - // Only create a given mapping once. If another version of insights is used elsewhere, that mapping will also exist - if (!scopeStack.node.tryFindChild(mapName)) { - // need to call findInMap here if we are going to set lazy=true, otherwise - // we get the informLazyUse info message - const map = new CfnMapping(scopeStack, mapName, { mapping, lazy: true }); - return map.findInMap(Aws.REGION, 'arn'); - } - // The ARN will be looked up at deployment time from the mapping we created - return Fn.findInMap(mapName, Aws.REGION, 'arn'); + return scopeStack.regionalFact(FactName.cloudwatchLambdaInsightsVersion(insightsVersion, arch)); } diff --git a/packages/@aws-cdk/aws-lambda/test/integ.lambda-insights-mapping.expected.json b/packages/@aws-cdk/aws-lambda/test/integ.lambda-insights-mapping.expected.json index e0975af7723cd..a7a94e5d13ba7 100644 --- a/packages/@aws-cdk/aws-lambda/test/integ.lambda-insights-mapping.expected.json +++ b/packages/@aws-cdk/aws-lambda/test/integ.lambda-insights-mapping.expected.json @@ -58,12 +58,15 @@ "Handler": "index.handler", "Layers": [ { - "Fn::FindInMap": [ - "LambdaInsightsVersions10540", - { - "Ref": "AWS::Region" - }, - "arn" + "Fn::Join": [ + "", + [ + "arn:aws:lambda:", + { + "Ref": "AWS::Region" + }, + ":580247275435:layer:LambdaInsightsExtension:2" + ] ] } ], @@ -131,12 +134,15 @@ "Handler": "index.handler", "Layers": [ { - "Fn::FindInMap": [ - "LambdaInsightsVersions10860", - { - "Ref": "AWS::Region" - }, - "arn" + "Fn::Join": [ + "", + [ + "arn:aws:lambda:", + { + "Ref": "AWS::Region" + }, + ":580247275435:layer:LambdaInsightsExtension:11" + ] ] } ], @@ -204,12 +210,15 @@ "Handler": "index.handler", "Layers": [ { - "Fn::FindInMap": [ - "LambdaInsightsVersions10890", - { - "Ref": "AWS::Region" - }, - "arn" + "Fn::Join": [ + "", + [ + "arn:aws:lambda:", + { + "Ref": "AWS::Region" + }, + ":580247275435:layer:LambdaInsightsExtension:12" + ] ] } ], @@ -278,11 +287,11 @@ "Layers": [ { "Fn::FindInMap": [ - "LambdaInsightsVersions10980", + "CloudwatchlambdainsightsversionMap", { "Ref": "AWS::Region" }, - "arn" + "1_0_98_0_x86_64" ] } ], @@ -351,11 +360,11 @@ "Layers": [ { "Fn::FindInMap": [ - "LambdaInsightsVersions101190", + "CloudwatchlambdainsightsversionMap", { "Ref": "AWS::Region" }, - "arn" + "1_0_119_0_x86_64" ] } ], @@ -426,12 +435,15 @@ "Handler": "index.handler", "Layers": [ { - "Fn::FindInMap": [ - "LambdaInsightsVersions101190arm64", - { - "Ref": "AWS::Region" - }, - "arn" + "Fn::Join": [ + "", + [ + "arn:aws:lambda:", + { + "Ref": "AWS::Region" + }, + ":580247275435:layer:LambdaInsightsExtension-Arm64:1" + ] ] } ], @@ -443,322 +455,94 @@ } }, "Mappings": { - "LambdaInsightsVersions10540": { - "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "ap-northeast-2": { - "arn": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:2" - }, - "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:2" - }, - "ca-central-1": { - "arn": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "eu-north-1": { - "arn": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:2" - }, - "eu-west-3": { - "arn": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:2" - }, - "sa-east-1": { - "arn": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:2" - }, - "us-west-1": { - "arn": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:2" - }, - "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:2" - } - }, - "LambdaInsightsVersions10860": { - "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "ap-northeast-2": { - "arn": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:11" - }, - "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:11" - }, - "ca-central-1": { - "arn": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "eu-north-1": { - "arn": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:11" - }, - "eu-west-3": { - "arn": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:11" - }, - "sa-east-1": { - "arn": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:11" - }, - "us-west-1": { - "arn": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:11" - }, - "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:11" - } - }, - "LambdaInsightsVersions10890": { - "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "ap-northeast-2": { - "arn": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:12" - }, - "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:12" - }, - "ca-central-1": { - "arn": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "eu-north-1": { - "arn": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:12" - }, - "eu-west-3": { - "arn": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:12" - }, - "sa-east-1": { - "arn": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:12" - }, - "us-west-1": { - "arn": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:12" - }, - "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:12" - } - }, - "LambdaInsightsVersions10980": { - "af-south-1": { - "arn": "arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:8" - }, - "ap-east-1": { - "arn": "arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:8" - }, - "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "ap-northeast-2": { - "arn": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:14" - }, - "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:14" - }, - "ca-central-1": { - "arn": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "cn-north-1": { - "arn": "arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:8" - }, - "cn-northwest-1": { - "arn": "arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:8" - }, - "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "eu-north-1": { - "arn": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "eu-south-1": { - "arn": "arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:8" - }, - "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:14" - }, - "eu-west-3": { - "arn": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:14" - }, - "me-south-1": { - "arn": "arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:8" - }, - "sa-east-1": { - "arn": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:14" - }, - "us-west-1": { - "arn": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:14" - }, - "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:14" - } - }, - "LambdaInsightsVersions101190": { + "CloudwatchlambdainsightsversionMap": { "af-south-1": { - "arn": "arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:9" }, "ap-east-1": { - "arn": "arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:9" }, "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:23" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:23" }, "ap-northeast-2": { - "arn": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:16" }, "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:16" }, "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:16" }, "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:16" }, "ca-central-1": { - "arn": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:16" }, "cn-north-1": { - "arn": "arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:9" }, "cn-northwest-1": { - "arn": "arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:9" }, "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:16" }, "eu-north-1": { - "arn": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:16" }, "eu-south-1": { - "arn": "arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:9" }, "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:16" }, "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:16" }, "eu-west-3": { - "arn": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:16" }, "me-south-1": { - "arn": "arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:9" + "1_0_98_0_x86_64": "arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:8", + "1_0_119_0_x86_64": "arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:9" }, "sa-east-1": { - "arn": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:16" }, "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:16" }, "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:16" + "1_0_98_0_x86_64": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:16" }, "us-west-1": { - "arn": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:16" - }, - "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:16" - } - }, - "LambdaInsightsVersions101190arm64": { - "ap-northeast-1": { - "arn": "arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "ap-south-1": { - "arn": "arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "ap-southeast-1": { - "arn": "arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "ap-southeast-2": { - "arn": "arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "eu-central-1": { - "arn": "arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "eu-west-1": { - "arn": "arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "eu-west-2": { - "arn": "arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "us-east-1": { - "arn": "arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:1" - }, - "us-east-2": { - "arn": "arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension-Arm64:1" + "1_0_98_0_x86_64": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:16" }, "us-west-2": { - "arn": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:1" + "1_0_98_0_x86_64": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:14", + "1_0_119_0_x86_64": "arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:16" } } } diff --git a/packages/@aws-cdk/aws-lambda/test/lambda-insights.test.ts b/packages/@aws-cdk/aws-lambda/test/lambda-insights.test.ts index 29bfee2f02615..581106e7a1f92 100644 --- a/packages/@aws-cdk/aws-lambda/test/lambda-insights.test.ts +++ b/packages/@aws-cdk/aws-lambda/test/lambda-insights.test.ts @@ -1,8 +1,9 @@ import '@aws-cdk/assert-internal/jest'; -import { MatchStyle } from '@aws-cdk/assert-internal'; +import { arrayWith, SynthUtils } from '@aws-cdk/assert-internal'; import * as ecr from '@aws-cdk/aws-ecr'; import * as cdk from '@aws-cdk/core'; import * as lambda from '../lib'; +import { Fact, FactName } from '@aws-cdk/region-info'; /** * Boilerplate code to create a Function with a given insights version @@ -14,6 +15,7 @@ function functionWithInsightsVersion( architecture?: lambda.Architecture, ): lambda.IFunction { return new lambda.Function(stack, id, { + functionName: id, code: new lambda.InlineCode('foo'), handler: 'index.handler', runtime: lambda.Runtime.NODEJS_10_X, @@ -77,27 +79,26 @@ describe('lambda-insights', () => { }); // AF-SOUTH-1 exists, 1.0.54.0 exists, but 1.0.54.0 isn't supported in AF-SOUTH-1 - functionWithInsightsVersion(stack, 'BadVersion', lambda.LambdaInsightsVersion.VERSION_1_0_54_0); - - // On synthesis it should throw an error - expect(() => app.synth()).toThrow('Insights version 1.0.54.0 is not supported in region af-south-1'); + expect(() => { + functionWithInsightsVersion(stack, 'BadVersion', lambda.LambdaInsightsVersion.VERSION_1_0_54_0); + }).toThrow('Insights version 1.0.54.0 is not supported in region af-south-1'); }); test('using a specific version without providing a region', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'Stack', {}); - functionWithInsightsVersion(stack, 'MyLambda', lambda.LambdaInsightsVersion.VERSION_1_0_54_0); + functionWithInsightsVersion(stack, 'MyLambda', lambda.LambdaInsightsVersion.VERSION_1_0_98_0); - // Should be looking up a mapping + // Still resolves because all elements of the mapping map to the same value expect(stack).toHaveResource('AWS::Lambda::Function', { Layers: [{ 'Fn::FindInMap': [ - 'LambdaInsightsVersions10540', + 'CloudwatchlambdainsightsversionMap', { Ref: 'AWS::Region', }, - 'arn', + '1_0_98_0_x86_64', ], }], }); @@ -111,212 +112,28 @@ describe('lambda-insights', () => { const app = new cdk.App(); const stack = new cdk.Stack(app, 'Stack', {}); - functionWithInsightsVersion(stack, 'MyLambda1', lambda.LambdaInsightsVersion.VERSION_1_0_54_0); - functionWithInsightsVersion(stack, 'MyLambda2', lambda.LambdaInsightsVersion.VERSION_1_0_54_0); - - /* eslint-disable quote-props */ - expect(stack).toMatchTemplate({ - Resources: { - MyLambda1ServiceRole69A7E1EA: { - 'Type': 'AWS::IAM::Role', - 'Properties': { - 'AssumeRolePolicyDocument': { - 'Statement': [ - { - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': { - 'Service': 'lambda.amazonaws.com', - }, - }, - ], - 'Version': '2012-10-17', - }, - 'ManagedPolicyArns': [ - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', - ], - ], - }, - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', - ], - ], - }, - ], - }, - }, - MyLambda1AAFB4554: { - 'Type': 'AWS::Lambda::Function', - 'Properties': { - 'Code': { - 'ZipFile': 'foo', - }, - 'Role': { - 'Fn::GetAtt': [ - 'MyLambda1ServiceRole69A7E1EA', - 'Arn', - ], - }, - 'Handler': 'index.handler', - 'Layers': [ - { - 'Fn::FindInMap': [ - 'LambdaInsightsVersions10540', - { - 'Ref': 'AWS::Region', - }, - 'arn', - ], - }, - ], - 'Runtime': 'nodejs10.x', - }, - 'DependsOn': [ - 'MyLambda1ServiceRole69A7E1EA', - ], - }, - MyLambda2ServiceRoleD09B370C: { - 'Type': 'AWS::IAM::Role', - 'Properties': { - 'AssumeRolePolicyDocument': { - 'Statement': [ - { - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': { - 'Service': 'lambda.amazonaws.com', - }, - }, - ], - 'Version': '2012-10-17', - }, - 'ManagedPolicyArns': [ - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', - ], - ], - }, - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', - ], - ], - }, - ], - }, - }, - MyLambda2254B54D5: { - 'Type': 'AWS::Lambda::Function', - 'Properties': { - 'Code': { - 'ZipFile': 'foo', - }, - 'Role': { - 'Fn::GetAtt': [ - 'MyLambda2ServiceRoleD09B370C', - 'Arn', - ], - }, - 'Handler': 'index.handler', - 'Layers': [ - { - 'Fn::FindInMap': [ - 'LambdaInsightsVersions10540', - { - 'Ref': 'AWS::Region', - }, - 'arn', - ], - }, - ], - 'Runtime': 'nodejs10.x', - }, - 'DependsOn': [ - 'MyLambda2ServiceRoleD09B370C', - ], - }, - }, - Mappings: { - LambdaInsightsVersions10540: { - 'ap-northeast-1': { - arn: 'arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'ap-northeast-2': { - 'arn': 'arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:2', - }, - 'ap-south-1': { - 'arn': 'arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'ap-southeast-1': { - 'arn': 'arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'ap-southeast-2': { - 'arn': 'arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:2', - }, - 'ca-central-1': { - 'arn': 'arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'eu-central-1': { - 'arn': 'arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'eu-north-1': { - 'arn': 'arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'eu-west-1': { - 'arn': 'arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'eu-west-2': { - 'arn': 'arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:2', - }, - 'eu-west-3': { - 'arn': 'arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:2', - }, - 'sa-east-1': { - 'arn': 'arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'us-east-1': { - 'arn': 'arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'us-east-2': { - 'arn': 'arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:2', - }, - 'us-west-1': { - 'arn': 'arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:2', - }, - 'us-west-2': { - 'arn': 'arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:2', - }, - }, - }, - }, MatchStyle.EXACT); + functionWithInsightsVersion(stack, 'MyLambda1', lambda.LambdaInsightsVersion.VERSION_1_0_98_0); + functionWithInsightsVersion(stack, 'MyLambda2', lambda.LambdaInsightsVersion.VERSION_1_0_98_0); + + expect(stack).toHaveResource('AWS::Lambda::Function', { + FunctionName: 'MyLambda1', + Layers: [{ + 'Fn::FindInMap': ['CloudwatchlambdainsightsversionMap', { Ref: 'AWS::Region' }, '1_0_98_0_x86_64'], + }], + }); + + expect(stack).toHaveResource('AWS::Lambda::Function', { + FunctionName: 'MyLambda2', + Layers: [{ + 'Fn::FindInMap': ['CloudwatchlambdainsightsversionMap', { Ref: 'AWS::Region' }, '1_0_98_0_x86_64'], + }], + }); + + const template = SynthUtils.toCloudFormation(stack); + expect(template.Mappings.CloudwatchlambdainsightsversionMap?.['af-south-1']).toEqual({ + '1_0_98_0_x86_64': 'arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:8', + }); + // On synthesis it should not throw an error expect(() => app.synth()).not.toThrow(); }); @@ -332,31 +149,23 @@ describe('lambda-insights', () => { expect(stack).toCountResources('AWS::Lambda::LayerVersion', 0); expect(stack).toHaveResourceLike('AWS::IAM::Role', { - 'AssumeRolePolicyDocument': { - 'Statement': [ + AssumeRolePolicyDocument: { + Statement: [ { - 'Action': 'sts:AssumeRole', - 'Principal': { - 'Service': 'lambda.amazonaws.com', - }, + Action: 'sts:AssumeRole', + Principal: { Service: 'lambda.amazonaws.com' }, }, ], }, - 'ManagedPolicyArns': [ - {}, + ManagedPolicyArns: arrayWith( { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', - ], - ], + 'Fn::Join': ['', [ + 'arn:', + { Ref: 'AWS::Partition' }, + ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', + ]], }, - ], + ), }); }); @@ -387,275 +196,44 @@ describe('lambda-insights', () => { const stack = new cdk.Stack(app, 'Stack', { env: { account: '123456789012', region: 'us-west-1' }, }); - functionWithInsightsVersion(stack, 'MyLambda', lambda.LambdaInsightsVersion.VERSION_1_0_119_0, lambda.Architecture.ARM_64); - // On synthesis it should not throw an error - expect(() => app.synth()).toThrow('Insights version 1.0.119.0 is not supported in region us-west-1'); + expect(() => { + functionWithInsightsVersion(stack, 'MyLambda', lambda.LambdaInsightsVersion.VERSION_1_0_119_0, lambda.Architecture.ARM_64); + }).toThrow('Insights version 1.0.119.0 is not supported in region us-west-1'); }); test('can create two functions, with different architectures in a region agnostic stack with the same version', () => { + // We mess with the fact database a bit here -- add a fact for the ARM LambdaInsights layer which + // is different from the existing facts, to force the region info to render a lookup table (instead + // of being able to just insert a literal). + Fact.register({ name: FactName.cloudwatchLambdaInsightsVersion('1.0.119.0', 'arm64'), region: 'eu-west-1', value: 'CompletelyDifferent' }, true); + const app = new cdk.App(); const stack = new cdk.Stack(app, 'Stack', {}); functionWithInsightsVersion(stack, 'MyLambda1', lambda.LambdaInsightsVersion.VERSION_1_0_119_0); functionWithInsightsVersion(stack, 'MyLambda2', lambda.LambdaInsightsVersion.VERSION_1_0_119_0, lambda.Architecture.ARM_64); - /* eslint-disable quote-props */ - expect(stack).toMatchTemplate({ - Resources: { - MyLambda1ServiceRole69A7E1EA: { - 'Type': 'AWS::IAM::Role', - 'Properties': { - 'AssumeRolePolicyDocument': { - 'Statement': [ - { - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': { - 'Service': 'lambda.amazonaws.com', - }, - }, - ], - 'Version': '2012-10-17', - }, - 'ManagedPolicyArns': [ - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', - ], - ], - }, - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', - ], - ], - }, - ], - }, - }, - MyLambda1AAFB4554: { - 'Type': 'AWS::Lambda::Function', - 'Properties': { - 'Code': { - 'ZipFile': 'foo', - }, - 'Role': { - 'Fn::GetAtt': [ - 'MyLambda1ServiceRole69A7E1EA', - 'Arn', - ], - }, - 'Handler': 'index.handler', - 'Layers': [ - { - 'Fn::FindInMap': [ - 'LambdaInsightsVersions101190', - { - 'Ref': 'AWS::Region', - }, - 'arn', - ], - }, - ], - 'Runtime': 'nodejs10.x', - }, - 'DependsOn': [ - 'MyLambda1ServiceRole69A7E1EA', - ], - }, - MyLambda2ServiceRoleD09B370C: { - 'Type': 'AWS::IAM::Role', - 'Properties': { - 'AssumeRolePolicyDocument': { - 'Statement': [ - { - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': { - 'Service': 'lambda.amazonaws.com', - }, - }, - ], - 'Version': '2012-10-17', - }, - 'ManagedPolicyArns': [ - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/service-role/AWSLambdaBasicExecutionRole', - ], - ], - }, - { - 'Fn::Join': [ - '', - [ - 'arn:', - { - 'Ref': 'AWS::Partition', - }, - ':iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy', - ], - ], - }, - ], - }, - }, - MyLambda2254B54D5: { - 'Type': 'AWS::Lambda::Function', - 'Properties': { - 'Code': { - 'ZipFile': 'foo', - }, - 'Role': { - 'Fn::GetAtt': [ - 'MyLambda2ServiceRoleD09B370C', - 'Arn', - ], - }, - 'Architectures': [ - 'arm64', - ], - 'Handler': 'index.handler', - 'Layers': [ - { - 'Fn::FindInMap': [ - 'LambdaInsightsVersions101190arm64', - { - 'Ref': 'AWS::Region', - }, - 'arn', - ], - }, - ], - 'Runtime': 'nodejs10.x', - }, - 'DependsOn': [ - 'MyLambda2ServiceRoleD09B370C', - ], - }, - }, - Mappings: { - LambdaInsightsVersions101190: { - 'af-south-1': { - 'arn': 'arn:aws:lambda:af-south-1:012438385374:layer:LambdaInsightsExtension:9', - }, - 'ap-east-1': { - 'arn': 'arn:aws:lambda:ap-east-1:519774774795:layer:LambdaInsightsExtension:9', - }, - 'ap-northeast-1': { - 'arn': 'arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension:23', - }, - 'ap-northeast-2': { - 'arn': 'arn:aws:lambda:ap-northeast-2:580247275435:layer:LambdaInsightsExtension:16', - }, - 'ap-south-1': { - 'arn': 'arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'ap-southeast-1': { - 'arn': 'arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'ap-southeast-2': { - 'arn': 'arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension:16', - }, - 'ca-central-1': { - 'arn': 'arn:aws:lambda:ca-central-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'cn-north-1': { - 'arn': 'arn:aws-cn:lambda:cn-north-1:488211338238:layer:LambdaInsightsExtension:9', - }, - 'cn-northwest-1': { - 'arn': 'arn:aws-cn:lambda:cn-northwest-1:488211338238:layer:LambdaInsightsExtension:9', - }, - 'eu-central-1': { - 'arn': 'arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'eu-north-1': { - 'arn': 'arn:aws:lambda:eu-north-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'eu-south-1': { - 'arn': 'arn:aws:lambda:eu-south-1:339249233099:layer:LambdaInsightsExtension:9', - }, - 'eu-west-1': { - 'arn': 'arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'eu-west-2': { - 'arn': 'arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension:16', - }, - 'eu-west-3': { - 'arn': 'arn:aws:lambda:eu-west-3:580247275435:layer:LambdaInsightsExtension:16', - }, - 'me-south-1': { - 'arn': 'arn:aws:lambda:me-south-1:285320876703:layer:LambdaInsightsExtension:9', - }, - 'sa-east-1': { - 'arn': 'arn:aws:lambda:sa-east-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'us-east-1': { - 'arn': 'arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'us-east-2': { - 'arn': 'arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension:16', - }, - 'us-west-1': { - 'arn': 'arn:aws:lambda:us-west-1:580247275435:layer:LambdaInsightsExtension:16', - }, - 'us-west-2': { - 'arn': 'arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension:16', - }, - }, - 'LambdaInsightsVersions101190arm64': { - 'ap-northeast-1': { - 'arn': 'arn:aws:lambda:ap-northeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'ap-south-1': { - 'arn': 'arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'ap-southeast-1': { - 'arn': 'arn:aws:lambda:ap-southeast-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'ap-southeast-2': { - 'arn': 'arn:aws:lambda:ap-southeast-2:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'eu-central-1': { - 'arn': 'arn:aws:lambda:eu-central-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'eu-west-1': { - 'arn': 'arn:aws:lambda:eu-west-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'eu-west-2': { - 'arn': 'arn:aws:lambda:eu-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'us-east-1': { - 'arn': 'arn:aws:lambda:us-east-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'us-east-2': { - 'arn': 'arn:aws:lambda:us-east-2:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - 'us-west-2': { - 'arn': 'arn:aws:lambda:us-west-2:580247275435:layer:LambdaInsightsExtension-Arm64:1', - }, - }, - }, - }, MatchStyle.EXACT); + expect(stack).toHaveResource('AWS::Lambda::Function', { + FunctionName: 'MyLambda1', + Layers: [{ + 'Fn::FindInMap': ['CloudwatchlambdainsightsversionMap', { Ref: 'AWS::Region' }, '1_0_119_0_x86_64'], + }], + }); + + expect(stack).toHaveResource('AWS::Lambda::Function', { + FunctionName: 'MyLambda2', + Layers: [{ + 'Fn::FindInMap': ['CloudwatchlambdainsightsversionMap', { Ref: 'AWS::Region' }, '1_0_119_0_arm64'], + }], + }); + + const template = SynthUtils.toCloudFormation(stack); + expect(template.Mappings.CloudwatchlambdainsightsversionMap?.['ap-south-1']).toEqual({ + '1_0_119_0_x86_64': 'arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension:16', + '1_0_119_0_arm64': 'arn:aws:lambda:ap-south-1:580247275435:layer:LambdaInsightsExtension-Arm64:1', + }); + // On synthesis it should not throw an error expect(() => app.synth()).not.toThrow(); }); diff --git a/packages/@aws-cdk/core/lib/private/region-lookup.ts b/packages/@aws-cdk/core/lib/private/region-lookup.ts new file mode 100644 index 0000000000000..208ff7eee30be --- /dev/null +++ b/packages/@aws-cdk/core/lib/private/region-lookup.ts @@ -0,0 +1,102 @@ +import * as cxapi from '@aws-cdk/cx-api'; +import { RegionInfo } from '@aws-cdk/region-info'; +import { CfnMapping } from '../cfn-mapping'; +import { Aws } from '../cfn-pseudo'; +import { Stack } from '../stack'; + +/** + * Make sure a CfnMapping exists in the given stack with the lookup values for the given fact + * + * Add to an existing CfnMapping if possible. + */ +export function deployTimeLookup(stack: Stack, factName: string, lookupMap: Record, defaultValue?: string) { + // If there are no lookups, just return the default + if (Object.values(lookupMap).length === 0) { + if (defaultValue === undefined) { + throw new Error(`region-info: don't have any information for ${factName}. Use 'Fact.register' to provide values, or add partitions to the '${cxapi.TARGET_PARTITIONS}' context value.`); + } + return defaultValue; + } + + // If the tokenized representation of all values is the same, we can just + // return the value directly and don't need to produce an actual map. + const pattern = findValuePattern(lookupMap); + if (pattern !== undefined) { + return pattern; + } + + // Derive map name and lookup key from the factName, splitting on ':' if it exists + const [factClass, factParam] = factName.includes(':') + ? factName.split(':') + : [factName, 'value'] as const; + + const mapId = `${ucfirst(factClass)}Map`; + const factKey = factParam.replace(/[^a-zA-Z0-9]/g, '_'); + + let mapping = stack.node.tryFindChild(mapId) as CfnMapping | undefined; + if (!mapping) { + mapping = new CfnMapping(stack, mapId); + } + for (const [region, value] of Object.entries(lookupMap)) { + mapping.setValue(region, factKey, value); + } + return mapping.findInMap(Aws.REGION, factKey); +} + +function ucfirst(x: string) { + return `${x.substr(0, 1).toUpperCase()}${x.substr(1)}`; +} + +/** + * Try to detect if all values in the map follow the same pattern + * + * Do this by replacing region and URLSuffix values in the found strings + * with their token variant. If at the end all strings have the same format, + * we can simplify to just the single value. + * + * This wouldn't have been necessary if the region-info library had encoded the + * pattern information instead of the literal values... but let's do it here now. + */ +function findValuePattern(regionMap: Record): string | undefined { + const simplified: Record = { ...regionMap }; + + // If they all contain URL_SUFFIX, substitute it, but only if the value is different + // among some values in the list (we don't want to tokenize unnecessarily, i.e. we don't + // want to replace `amazonaws.com` with URL_SUFFIX if it's not necessary) + const urlSuffixes = Object.keys(simplified).map(urlSuffix); + if (!allSame(urlSuffixes) && Object.entries(simplified).every(([region, value]) => value.includes(urlSuffix(region)))) { + for (const region in simplified) { + simplified[region] = replaceAll(simplified[region], urlSuffix(region), Aws.URL_SUFFIX); + } + } + + // If they all contain REGION, substitute it (no need to do the "is everything different" + // check, this is true by design for these values) + if (Object.entries(simplified).every(([region, value]) => value.includes(region))) { + for (const region in simplified) { + simplified[region] = replaceAll(simplified[region], region, Aws.REGION); + } + } + + // If the values are now all the same, return the singleton value + const values = Object.values(simplified); + if (allSame(values)) { + return values[0]; + } + + // Otherwise we failed + return undefined; +} + +function allSame(xs: string[]) { + return xs.every((x) => x === xs[0]); +} + +function urlSuffix(region: string) { + return RegionInfo.get(region)?.domainSuffix ?? 'amazonaws.com'; +} + +function replaceAll(x: string, pat: string, replacement: string) { + return x.split(pat).join(replacement); +} + diff --git a/packages/@aws-cdk/core/lib/stack.ts b/packages/@aws-cdk/core/lib/stack.ts index 6645805519b81..0efd45fc14b3f 100644 --- a/packages/@aws-cdk/core/lib/stack.ts +++ b/packages/@aws-cdk/core/lib/stack.ts @@ -803,6 +803,45 @@ export class Stack extends CoreConstruct implements ITaggable { } } + /** + * Look up a fact value for the given fact for the region of this stack + * + * Will return a definite value only if the region of the current stack is resolved. + * If not, a lookup map will be added to the stack and the lookup will be done at + * CDK deployment time. + * + * What regions will be included in the lookup map is controlled by the + * `@aws-cdk/core:target-partitions` context value: it must be set to a list + * of partitions, and only regions from the given partitions will be included. + * If no such context key is set, all regions will be included. + * + * This function is intended to be used by construct library authors. Application + * builders can rely on the abstractions offered by construct libraries and do + * not have to worry about regional facts. + * + * If `defaultValue` is not given, it is an error if the fact is unknown for + * the given region. + */ + public regionalFact(factName: string, defaultValue?: string): string { + if (!Token.isUnresolved(this.region)) { + const ret = Fact.find(this.region, factName) ?? defaultValue; + if (ret === undefined) { + throw new Error(`region-info: don't know ${factName} for region ${this.region}. Use 'Fact.register' to provide this value.`); + } + return ret; + } + + const partitions = Node.of(this).tryGetContext(cxapi.TARGET_PARTITIONS); + if (partitions !== undefined && !Array.isArray(partitions)) { + throw new Error(`Context value '${cxapi.TARGET_PARTITIONS}' should be a list of strings, got: ${JSON.stringify(cxapi.TARGET_PARTITIONS)}`); + } + + const lookupMap = partitions ? RegionInfo.limitedRegionMap(factName, partitions) : RegionInfo.regionMap(factName); + + return deployTimeLookup(this, factName, lookupMap, defaultValue); + } + + /** * Create a CloudFormation Export for a value * @@ -1313,3 +1352,6 @@ import { Stage } from './stage'; import { ITaggable, TagManager } from './tag-manager'; import { Token, Tokenization } from './token'; import { referenceNestedStackValueInParent } from './private/refs'; +import { Fact, RegionInfo } from '@aws-cdk/region-info'; +import { deployTimeLookup } from './private/region-lookup'; + diff --git a/packages/@aws-cdk/core/test/stack.test.ts b/packages/@aws-cdk/core/test/stack.test.ts index 160ff09278b50..b344dbbbff0be 100644 --- a/packages/@aws-cdk/core/test/stack.test.ts +++ b/packages/@aws-cdk/core/test/stack.test.ts @@ -1,10 +1,13 @@ import { testDeprecated, testFutureBehavior, testLegacyBehavior } from '@aws-cdk/cdk-build-tools'; import * as cxapi from '@aws-cdk/cx-api'; +import { Fact } from '@aws-cdk/region-info'; +import { Node } from 'constructs'; import { App, CfnCondition, CfnInclude, CfnOutput, CfnParameter, CfnResource, Construct, Lazy, ScopedAws, Stack, validateString, ISynthesisSession, Tags, LegacyStackSynthesizer, DefaultStackSynthesizer, NestedStack, + Aws, } from '../lib'; import { Intrinsic } from '../lib/private/intrinsic'; import { resolveReferences } from '../lib/private/refs'; @@ -1190,6 +1193,58 @@ describe('stack', () => { }); }); +describe('regionalFact', () => { + Fact.register({ name: 'MyFact', region: 'us-east-1', value: 'x.amazonaws.com' }); + Fact.register({ name: 'MyFact', region: 'eu-west-1', value: 'x.amazonaws.com' }); + Fact.register({ name: 'MyFact', region: 'cn-north-1', value: 'x.amazonaws.com.cn' }); + + Fact.register({ name: 'WeirdFact', region: 'us-east-1', value: 'oneformat' }); + Fact.register({ name: 'WeirdFact', region: 'eu-west-1', value: 'otherformat' }); + + test('regional facts return a literal value if possible', () => { + const stack = new Stack(undefined, 'Stack', { env: { region: 'us-east-1' } }); + expect(stack.regionalFact('MyFact')).toEqual('x.amazonaws.com'); + }); + + test('regional facts are simplified to use URL_SUFFIX token if possible', () => { + const stack = new Stack(); + expect(stack.regionalFact('MyFact')).toEqual(`x.${Aws.URL_SUFFIX}`); + }); + + test('regional facts are simplified to use concrete values if URL_SUFFIX token is not necessary', () => { + const stack = new Stack(); + Node.of(stack).setContext(cxapi.TARGET_PARTITIONS, ['aws']); + expect(stack.regionalFact('MyFact')).toEqual('x.amazonaws.com'); + }); + + test('regional facts generate a mapping if necessary', () => { + const stack = new Stack(); + new CfnOutput(stack, 'TheFact', { + value: stack.regionalFact('WeirdFact'), + }); + + expect(toCloudFormation(stack)).toEqual({ + Mappings: { + WeirdFactMap: { + 'eu-west-1': { value: 'otherformat' }, + 'us-east-1': { value: 'oneformat' }, + }, + }, + Outputs: { + TheFact: { + Value: { + 'Fn::FindInMap': [ + 'WeirdFactMap', + { Ref: 'AWS::Region' }, + 'value', + ], + }, + }, + }, + }); + }); +}); + class StackWithPostProcessor extends Stack { // ... diff --git a/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts b/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts index acaaeb3a73ef6..f9b683417dc94 100644 --- a/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts +++ b/packages/@aws-cdk/custom-resources/test/provider-framework/waiter-state-machine.test.ts @@ -1,12 +1,15 @@ import '@aws-cdk/assert-internal/jest'; import { Code, Function as lambdaFn, Runtime } from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; +import { Node } from 'constructs'; import { WaiterStateMachine } from '../../lib/provider-framework/waiter-state-machine'; describe('state machine', () => { test('contains the needed resources', () => { // GIVEN const stack = new Stack(); + Node.of(stack).setContext('@aws-cdk/core:target-partitions', ['aws', 'aws-cn']); + const isCompleteHandler = new lambdaFn(stack, 'isComplete', { code: Code.fromInline('foo'), runtime: Runtime.NODEJS_12_X, diff --git a/packages/@aws-cdk/cx-api/lib/features.ts b/packages/@aws-cdk/cx-api/lib/features.ts index 37c9aace0ce86..74f4ff63082a6 100644 --- a/packages/@aws-cdk/cx-api/lib/features.ts +++ b/packages/@aws-cdk/cx-api/lib/features.ts @@ -160,6 +160,16 @@ export const LAMBDA_RECOGNIZE_VERSION_PROPS = '@aws-cdk/aws-lambda:recognizeVers */ export const CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021 = '@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021'; +/** + * What regions to include in lookup tables of environment agnostic stacks + * + * Has no effect on stacks that have a defined region, but will limit the amount + * of unnecessary regions included in stacks without a known region. + * + * The type of this value should be a list of strings. + */ +export const TARGET_PARTITIONS = '@aws-cdk/core:target-partitions'; + /** * This map includes context keys and values for feature flags that enable * capabilities "from the future", which we could not introduce as the default @@ -173,7 +183,7 @@ export const CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021 = '@aws-cdk/aws-cl * * Tests must cover the default (disabled) case and the future (enabled) case. */ -export const FUTURE_FLAGS: { [key: string]: any } = { +export const FUTURE_FLAGS: { [key: string]: boolean } = { [APIGATEWAY_USAGEPLANKEY_ORDERINSENSITIVE_ID]: true, [ENABLE_STACK_NAME_DUPLICATES_CONTEXT]: true, [ENABLE_DIFF_NO_FAIL_CONTEXT]: true, @@ -192,6 +202,13 @@ export const FUTURE_FLAGS: { [key: string]: any } = { // [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true', }; +/** + * Values that will be set by default in a new project, which are not necessarily booleans (and don't expire) + */ +export const NEW_PROJECT_DEFAULT_CONTEXT: { [key: string]: any} = { + [TARGET_PARTITIONS]: ['aws', 'aws-cn'], +}; + /** * The list of future flags that are now expired. This is going to be used to identify * and block usages of old feature flags in the new major version of CDK. diff --git a/packages/@aws-cdk/region-info/build-tools/aws-entities.ts b/packages/@aws-cdk/region-info/build-tools/aws-entities.ts deleted file mode 100644 index 28c59828f7477..0000000000000 --- a/packages/@aws-cdk/region-info/build-tools/aws-entities.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * The names of all (known) AWS regions - * - * Not in the list ==> no built-in data for that region. - */ -export const AWS_REGIONS = [ - 'af-south-1', // Africa (Cape Town) - 'ap-east-1', // Asia Pacific (Hong Kong) - 'ap-northeast-1', // Asia Pacific (Tokyo) - 'ap-northeast-2', // Asia Pacific (Seoul) - 'ap-northeast-3', // Asia Pacific (Osaka) - 'ap-south-1', // Asia Pacific (Mumbai) - 'ap-southeast-1', // Asia Pacific (Singapore) - 'ap-southeast-2', // Asia Pacific (Sydney) - 'ca-central-1', // Canada (Central) - 'cn-north-1', // China (Beijing) - 'cn-northwest-1', // China (Ningxia) - 'eu-central-1', // Europe (Frankfurt) - 'eu-north-1', // Europe (Stockholm) - 'eu-south-1', // Europe (Milan) - 'eu-west-1', // Europe (Ireland) - 'eu-west-2', // Europe (London) - 'eu-west-3', // Europe (Paris) - 'me-south-1', // Middle East (Bahrain) - 'sa-east-1', // South America (São Paulo) - 'us-east-1', // US East (N. Virginia) - 'us-east-2', // US East (Ohio) - 'us-gov-east-1', // AWS GovCloud (US-East) - 'us-gov-west-1', // AWS GovCloud (US-West) - 'us-iso-east-1', // AWS ISO - 'us-isob-east-1', // AWS ISO-B - 'us-west-1', // US West (N. California) - 'us-west-2', // US West (Oregon) -].sort(); - -/** - * Possibly non-exaustive list of all service names, used to locate service principals. - * - * Not in the list ==> default service principal mappings. - */ -export const AWS_SERVICES = [ - 'application-autoscaling', - 'autoscaling', - 'codedeploy', - 'ec2', - 'events', - 'lambda', - 'logs', - 's3', - 'sns', - 'sqs', - 'states', -].sort(); diff --git a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts index 6f3b0abd737f1..e641f654ba532 100644 --- a/packages/@aws-cdk/region-info/build-tools/fact-tables.ts +++ b/packages/@aws-cdk/region-info/build-tools/fact-tables.ts @@ -1,15 +1,3 @@ -export const AWS_OLDER_REGIONS = new Set([ - 'us-east-1', - 'us-west-1', - 'us-west-2', - 'us-gov-west-1', - 'ap-southeast-1', - 'ap-southeast-2', - 'ap-northeast-1', - 'sa-east-1', - 'eu-west-1', -]); - export const AWS_CDK_METADATA = new Set([ 'us-east-2', 'us-east-1', diff --git a/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts b/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts index 23cdbc85071bb..3eea4e1fc4387 100644 --- a/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts +++ b/packages/@aws-cdk/region-info/build-tools/generate-static-data.ts @@ -1,9 +1,14 @@ import * as path from 'path'; import * as fs from 'fs-extra'; +import { + AWS_REGIONS, + AWS_SERVICES, + before, + RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN, +} from '../lib/aws-entities'; import { Default } from '../lib/default'; -import { AWS_REGIONS, AWS_SERVICES } from './aws-entities'; import { - APPMESH_ECR_ACCOUNTS, AWS_CDK_METADATA, AWS_OLDER_REGIONS, CLOUDWATCH_LAMBDA_INSIGHTS_ARNS, DLC_REPOSITORY_ACCOUNTS, + APPMESH_ECR_ACCOUNTS, AWS_CDK_METADATA, CLOUDWATCH_LAMBDA_INSIGHTS_ARNS, DLC_REPOSITORY_ACCOUNTS, ELBV2_ACCOUNTS, FIREHOSE_CIDR_BLOCKS, PARTITION_MAP, ROUTE_53_BUCKET_WEBSITE_ZONE_IDS, EBS_ENV_ENDPOINT_HOSTED_ZONE_IDS, } from './fact-tables'; @@ -51,7 +56,7 @@ async function main(): Promise { registerFact(region, 'CDK_METADATA_RESOURCE_AVAILABLE', AWS_CDK_METADATA.has(region) ? 'YES' : 'NO'); - registerFact(region, 'S3_STATIC_WEBSITE_ENDPOINT', AWS_OLDER_REGIONS.has(region) + registerFact(region, 'S3_STATIC_WEBSITE_ENDPOINT', before(region, RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN) ? `s3-website-${region}.${domainSuffix}` : `s3-website.${region}.${domainSuffix}`); @@ -59,7 +64,6 @@ async function main(): Promise { registerFact(region, 'EBS_ENV_ENDPOINT_HOSTED_ZONE_ID', EBS_ENV_ENDPOINT_HOSTED_ZONE_IDS[region] || ''); - registerFact(region, 'ELBV2_ACCOUNT', ELBV2_ACCOUNTS[region]); registerFact(region, 'DLC_REPOSITORY_ACCOUNT', DLC_REPOSITORY_ACCOUNTS[region]); diff --git a/packages/@aws-cdk/region-info/lib/aws-entities.ts b/packages/@aws-cdk/region-info/lib/aws-entities.ts new file mode 100644 index 0000000000000..30d8bc8c2c1d4 --- /dev/null +++ b/packages/@aws-cdk/region-info/lib/aws-entities.ts @@ -0,0 +1,147 @@ +// Rule prefix +const RULE_ = 'RULE_'; + +/** + * After this point, SSM only creates regional principals + */ +export const RULE_SSM_PRINCIPALS_ARE_REGIONAL = `${RULE_}SSM_PRINCIPALS_ARE_REGIONAL`; + +/** + * After this point, S3 website domains look like `s3-website.REGION.s3.amazonaws.com` + * + * Before this point, S3 website domains look like `s3-website-REGION.s3.amazonaws.com`. + */ +export const RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN = `${RULE_}SSM_PRINCIPALS_ARE_REGIONAL`; + +/** + * List of AWS region, ordered by launch date (oldest to newest) + * + * The significance of this is that standards and conventions change over time. + * Generally, as rules are changed they only apply to new regions, and old + * regions are left as-is. + * + * We mix the list of regions with a list of rules that were introduced over + * time (rules are strings starting with `RULE_`). + * + * Therefore, if we want to know if a rule applies to a certain region, we + * only need to check its position in the list and compare it to when a + * rule was introduced. + */ +export const AWS_REGIONS_AND_RULES = [ + 'us-east-1', // US East (N. Virginia) + 'eu-west-1', // Europe (Ireland) + 'us-west-1', // US West (N. California) + 'ap-southeast-1', // Asia Pacific (Singapore) + 'ap-northeast-1', // Asia Pacific (Tokyo) + 'us-gov-west-1', // AWS GovCloud (US-West) + 'us-west-2', // US West (Oregon) + 'sa-east-1', // South America (São Paulo) + 'ap-southeast-2', // Asia Pacific (Sydney) + RULE_S3_WEBSITE_REGIONAL_SUBDOMAIN, + 'cn-north-1', // China (Beijing) + 'us-iso-east-1', // AWS ISO + 'eu-central-1', // Europe (Frankfurt) + 'ap-northeast-2', // Asia Pacific (Seoul) + 'ap-south-1', // Asia Pacific (Mumbai) + 'us-east-2', // US East (Ohio) + 'ca-central-1', // Canada (Central) + 'eu-west-2', // Europe (London) + 'us-isob-east-1', // AWS ISO-B + 'cn-northwest-1', // China (Ningxia) + 'eu-west-3', // Europe (Paris) + 'ap-northeast-3', // Asia Pacific (Osaka) + 'us-gov-east-1', // AWS GovCloud (US-East) + 'eu-north-1', // Europe (Stockholm) + RULE_SSM_PRINCIPALS_ARE_REGIONAL, + 'ap-east-1', // Asia Pacific (Hong Kong) + 'me-south-1', // Middle East (Bahrain) + 'eu-south-1', // Europe (Milan) + 'af-south-1', // Africa (Cape Town) + 'us-iso-west-1', // US ISO West + 'eu-south-2', // Europe (Spain) + 'ap-southeast-3', // Asia Pacific (Jakarta) +]; + +/** + * The names of all (known) AWS regions + * + * Not in the list ==> no built-in data for that region. + */ +export const AWS_REGIONS = AWS_REGIONS_AND_RULES + .filter((x) => !x.startsWith(RULE_)) + .sort(); + +/** + * Possibly non-exaustive list of all service names, used to locate service principals. + * + * Not in the list ==> default service principal mappings. + */ +export const AWS_SERVICES = [ + 'application-autoscaling', + 'autoscaling', + 'codedeploy', + 'ec2', + 'events', + 'lambda', + 'logs', + 's3', + 'ssm', + 'sns', + 'sqs', + 'states', +].sort(); + +/** + * Whether or not a region predates a given rule (or region). + * + * Unknown region => we have to assume no. + */ +export function before(region: string, ruleOrRegion: string) { + const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion); + if (ruleIx === -1) { + throw new Error(`Unknown rule: ${ruleOrRegion}`); + } + const regionIx = AWS_REGIONS_AND_RULES.indexOf(region); + return regionIx === -1 ? false : regionIx < ruleIx; +} + +/** + * Return all regions before a given rule was introduced (or region) + */ +export function regionsBefore(ruleOrRegion: string): string[] { + const ruleIx = AWS_REGIONS_AND_RULES.indexOf(ruleOrRegion); + if (ruleIx === -1) { + throw new Error(`Unknown rule: ${ruleOrRegion}`); + } + return AWS_REGIONS_AND_RULES.filter((_, i) => i < ruleIx).sort(); +} + +export interface Region { partition: string, domainSuffix: string } + +const PARTITION_MAP: { [region: string]: Region } = { + 'default': { partition: 'aws', domainSuffix: 'amazonaws.com' }, + 'cn-': { partition: 'aws-cn', domainSuffix: 'amazonaws.com.cn' }, + 'us-gov-': { partition: 'aws-us-gov', domainSuffix: 'amazonaws.com' }, + 'us-iso-': { partition: 'aws-iso', domainSuffix: 'c2s.ic.gov' }, + 'us-isob-': { partition: 'aws-iso-b', domainSuffix: 'sc2s.sgov.gov' }, +}; + +export function partitionInformation(region: string): Region { + for (const [prefix, info] of Object.entries(PARTITION_MAP)) { + if (region.startsWith(prefix)) { + return info; + } + } + return PARTITION_MAP.default; +} + +/** + * Build a lookup map for all regions + */ +export function generateRegionMap(cb: (region: string) => string): Record { + const ret: Record = {}; + for (const region of AWS_REGIONS) { + ret[region] = cb(region); + } + return ret; +} \ No newline at end of file diff --git a/packages/@aws-cdk/region-info/lib/default.ts b/packages/@aws-cdk/region-info/lib/default.ts index c0ae0cc2b28d5..b11aa881f955c 100644 --- a/packages/@aws-cdk/region-info/lib/default.ts +++ b/packages/@aws-cdk/region-info/lib/default.ts @@ -1,3 +1,5 @@ +import { before, RULE_SSM_PRINCIPALS_ARE_REGIONAL } from './aws-entities'; + /** * Provides default values for certain regional information points. */ @@ -16,24 +18,22 @@ export class Default { * all you have is `{ "Ref": "AWS::Region" }`). This way you get the same defaulting behavior that is normally used * for built-in data. * - * @param service the name of the service (s3, s3.amazonaws.com, ...) + * @param serviceFqn the name of the service (s3, s3.amazonaws.com, ...) * @param region the region in which the service principal is needed. - * @param urlSuffix the URL suffix for the partition in which the region is located. + * @param urlSuffix deprecated and ignored. */ - public static servicePrincipal(service: string, region: string, urlSuffix: string): string { - const matches = service.match(/^([^.]+)(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))?$/); - if (!matches) { + public static servicePrincipal(serviceFqn: string, region: string, urlSuffix: string): string { + const service = extractSimpleName(serviceFqn); + if (!service) { // Return "service" if it does not look like any of the following: // - s3 // - s3.amazonaws.com // - s3.amazonaws.com.cn // - s3.c2s.ic.gov // - s3.sc2s.sgov.gov - return service; + return serviceFqn; } - service = matches[1]; // Simplify the service name down to something like "s3" - // Exceptions for Service Principals in us-iso-* const US_ISO_EXCEPTIONS = new Set([ 'cloudhsm', @@ -42,12 +42,6 @@ export class Default { 'workspaces', ]); - // Exceptions for Service Principals in us-isob-* - const US_ISOB_EXCEPTIONS = new Set([ - 'dms', - 'states', - ]); - // Account for idiosyncratic Service Principals in `us-iso-*` regions if (region.startsWith('us-iso-') && US_ISO_EXCEPTIONS.has(service)) { switch (service) { @@ -61,6 +55,12 @@ export class Default { } } + // Exceptions for Service Principals in us-isob-* + const US_ISOB_EXCEPTIONS = new Set([ + 'dms', + 'states', + ]); + // Account for idiosyncratic Service Principals in `us-isob-*` regions if (region.startsWith('us-isob-') && US_ISOB_EXCEPTIONS.has(service)) { switch (service) { @@ -74,6 +74,13 @@ export class Default { } } + // SSM turned from global to regional at some point + if (service === 'ssm') { + return before(region, RULE_SSM_PRINCIPALS_ARE_REGIONAL) + ? `${service}.amazonaws.com` + : `${service}.${region}.amazonaws.com`; + } + switch (service) { // Services with a regional AND partitional principal case 'codedeploy': @@ -82,7 +89,6 @@ export class Default { // Services with a regional principal case 'states': - case 'ssm': return `${service}.${region}.amazonaws.com`; // Services with a partitional principal @@ -98,3 +104,8 @@ export class Default { private constructor() { } } + +function extractSimpleName(serviceFqn: string) { + const matches = serviceFqn.match(/^([^.]+)(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))?$/); + return matches ? matches[1] : undefined; +} \ No newline at end of file diff --git a/packages/@aws-cdk/region-info/lib/fact.ts b/packages/@aws-cdk/region-info/lib/fact.ts index 498e33d693abf..0c4d12a53bec4 100644 --- a/packages/@aws-cdk/region-info/lib/fact.ts +++ b/packages/@aws-cdk/region-info/lib/fact.ts @@ -1,3 +1,5 @@ +import { AWS_REGIONS } from './aws-entities'; + /** * A database of regional information. */ @@ -7,7 +9,7 @@ export class Fact { * may not be an exhaustive list of all available AWS regions. */ public static get regions(): string[] { - return Object.keys(this.database); + return AWS_REGIONS; } /** diff --git a/packages/@aws-cdk/region-info/lib/region-info.ts b/packages/@aws-cdk/region-info/lib/region-info.ts index 35dce3d6ca980..eba53d435e9be 100644 --- a/packages/@aws-cdk/region-info/lib/region-info.ts +++ b/packages/@aws-cdk/region-info/lib/region-info.ts @@ -1,3 +1,4 @@ +import { partitionInformation } from './aws-entities'; import { Fact, FactName } from './fact'; /** @@ -31,6 +32,26 @@ export class RegionInfo { return ret; } + /** + * Retrieves a collection of all fact values for all regions, limited to some partitions + * + * @param factName the name of the fact to retrieve values for. + * For a list of common fact names, see the FactName class + * @param partitions list of partitions to retrieve facts for. Defaults + * to `['aws', 'aws-cn']`. + * @returns a mapping with AWS region codes as the keys, + * and the fact in the given region as the value for that key + */ + public static limitedRegionMap(factName: string, partitions: string[]): { [region: string]: string } { + const ret: Record = {}; + for (const [region, value] of Object.entries(RegionInfo.regionMap(factName))) { + if (partitions.includes(partitionInformation(region).partition)) { + ret[region] = value; + } + } + return ret; + } + /** * Obtain region info for a given region name. * diff --git a/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap b/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap index 39b8766351e37..32fdb8a6138fb 100644 --- a/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap +++ b/packages/@aws-cdk/region-info/test/__snapshots__/region-info.test.js.snap @@ -28,6 +28,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.af-south-1.amazonaws.com", "states": "states.af-south-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -58,6 +59,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ap-east-1.amazonaws.com", "states": "states.ap-east-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -88,6 +90,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.ap-northeast-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -118,6 +121,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ap-northeast-2.amazonaws.com", "states": "states.ap-northeast-2.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -148,6 +152,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ap-northeast-3.amazonaws.com", "states": "states.ap-northeast-3.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -178,6 +183,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ap-south-1.amazonaws.com", "states": "states.ap-south-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -208,6 +214,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.ap-southeast-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -238,10 +245,42 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.ap-southeast-2.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", }, + "ap-southeast-3": Object { + "cdkMetadataResourceAvailable": false, + "domainSuffix": "amazonaws.com", + "lambdaInsightsArmVersions": Object { + "1.0.119.0": undefined, + }, + "lambdaInsightsVersions": Object { + "1.0.119.0": undefined, + "1.0.54.0": undefined, + "1.0.86.0": undefined, + "1.0.89.0": undefined, + "1.0.98.0": undefined, + }, + "partition": "aws", + "s3StaticWebsiteEndpoint": "s3-website.ap-southeast-3.amazonaws.com", + "servicePrincipals": Object { + "application-autoscaling": "application-autoscaling.amazonaws.com", + "autoscaling": "autoscaling.amazonaws.com", + "codedeploy": "codedeploy.ap-southeast-3.amazonaws.com", + "ec2": "ec2.amazonaws.com", + "events": "events.amazonaws.com", + "lambda": "lambda.amazonaws.com", + "logs": "logs.ap-southeast-3.amazonaws.com", + "s3": "s3.amazonaws.com", + "sns": "sns.amazonaws.com", + "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ap-southeast-3.amazonaws.com", + "states": "states.ap-southeast-3.amazonaws.com", + }, + "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", + }, "ca-central-1": Object { "cdkMetadataResourceAvailable": true, "domainSuffix": "amazonaws.com", @@ -268,6 +307,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.ca-central-1.amazonaws.com", "states": "states.ca-central-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -298,6 +338,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.cn-north-1.amazonaws.com", "states": "states.cn-north-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "cn.com.amazonaws.vpce", @@ -328,6 +369,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.cn-northwest-1.amazonaws.com", "states": "states.cn-northwest-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "cn.com.amazonaws.vpce", @@ -358,6 +400,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-central-1.amazonaws.com", "states": "states.eu-central-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -388,6 +431,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-north-1.amazonaws.com", "states": "states.eu-north-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -418,10 +462,42 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-south-1.amazonaws.com", "states": "states.eu-south-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", }, + "eu-south-2": Object { + "cdkMetadataResourceAvailable": false, + "domainSuffix": "amazonaws.com", + "lambdaInsightsArmVersions": Object { + "1.0.119.0": undefined, + }, + "lambdaInsightsVersions": Object { + "1.0.119.0": undefined, + "1.0.54.0": undefined, + "1.0.86.0": undefined, + "1.0.89.0": undefined, + "1.0.98.0": undefined, + }, + "partition": "aws", + "s3StaticWebsiteEndpoint": "s3-website.eu-south-2.amazonaws.com", + "servicePrincipals": Object { + "application-autoscaling": "application-autoscaling.amazonaws.com", + "autoscaling": "autoscaling.amazonaws.com", + "codedeploy": "codedeploy.eu-south-2.amazonaws.com", + "ec2": "ec2.amazonaws.com", + "events": "events.amazonaws.com", + "lambda": "lambda.amazonaws.com", + "logs": "logs.eu-south-2.amazonaws.com", + "s3": "s3.amazonaws.com", + "sns": "sns.amazonaws.com", + "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-south-2.amazonaws.com", + "states": "states.eu-south-2.amazonaws.com", + }, + "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", + }, "eu-west-1": Object { "cdkMetadataResourceAvailable": true, "domainSuffix": "amazonaws.com", @@ -448,6 +524,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.eu-west-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -478,6 +555,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-west-2.amazonaws.com", "states": "states.eu-west-2.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -508,6 +586,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.eu-west-3.amazonaws.com", "states": "states.eu-west-3.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -538,6 +617,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.me-south-1.amazonaws.com", "states": "states.me-south-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -568,6 +648,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.sa-east-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -598,6 +679,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.us-east-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -628,6 +710,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.us-east-2.amazonaws.com", "states": "states.us-east-2.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -658,6 +741,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.us-gov-east-1.amazonaws.com", "states": "states.us-gov-east-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -688,6 +772,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.us-gov-west-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -718,6 +803,38 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.us-iso-east-1.amazonaws.com", + "states": "states.amazonaws.com", + }, + "vpcEndPointServiceNamePrefix": "gov.ic.c2s.vpce", + }, + "us-iso-west-1": Object { + "cdkMetadataResourceAvailable": false, + "domainSuffix": "c2s.ic.gov", + "lambdaInsightsArmVersions": Object { + "1.0.119.0": undefined, + }, + "lambdaInsightsVersions": Object { + "1.0.119.0": undefined, + "1.0.54.0": undefined, + "1.0.86.0": undefined, + "1.0.89.0": undefined, + "1.0.98.0": undefined, + }, + "partition": "aws-iso", + "s3StaticWebsiteEndpoint": "s3-website.us-iso-west-1.c2s.ic.gov", + "servicePrincipals": Object { + "application-autoscaling": "application-autoscaling.amazonaws.com", + "autoscaling": "autoscaling.amazonaws.com", + "codedeploy": "codedeploy.us-iso-west-1.c2s.ic.gov", + "ec2": "ec2.c2s.ic.gov", + "events": "events.amazonaws.com", + "lambda": "lambda.amazonaws.com", + "logs": "logs.us-iso-west-1.c2s.ic.gov", + "s3": "s3.amazonaws.com", + "sns": "sns.amazonaws.com", + "sqs": "sqs.amazonaws.com", + "ssm": "ssm.us-iso-west-1.amazonaws.com", "states": "states.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "gov.ic.c2s.vpce", @@ -748,6 +865,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.us-isob-east-1.amazonaws.com", "states": "states.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "gov.sgov.sc2s.vpce", @@ -778,6 +896,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.us-west-1.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", @@ -808,6 +927,7 @@ Object { "s3": "s3.amazonaws.com", "sns": "sns.amazonaws.com", "sqs": "sqs.amazonaws.com", + "ssm": "ssm.amazonaws.com", "states": "states.us-west-2.amazonaws.com", }, "vpcEndPointServiceNamePrefix": "com.amazonaws.vpce", diff --git a/packages/@aws-cdk/region-info/test/default.test.ts b/packages/@aws-cdk/region-info/test/default.test.ts index 1e7c1b166c9b6..7ee01e802a5bd 100644 --- a/packages/@aws-cdk/region-info/test/default.test.ts +++ b/packages/@aws-cdk/region-info/test/default.test.ts @@ -47,4 +47,14 @@ describe('servicePrincipal', () => { expect(Default.servicePrincipal(`${service}.amazonaws.com`, 'us-iso-east-1', 'c2s.ic.gov')).toBe(`${service}.c2s.ic.gov`); }); } + }); + + +describe('spot-check some service principals', () => { + test('ssm', () => { + expect(Default.servicePrincipal('ssm.amazonaws.com', 'us-east-1', 'x')).toBe('ssm.amazonaws.com'); + expect(Default.servicePrincipal('ssm.amazonaws.com', 'ap-east-1', 'x')).toBe('ssm.ap-east-1.amazonaws.com'); + expect(Default.servicePrincipal('ssm.amazonaws.com', 'eu-south-1', 'x')).toBe('ssm.eu-south-1.amazonaws.com'); + }); +}); \ No newline at end of file diff --git a/packages/@aws-cdk/region-info/test/fact.test.ts b/packages/@aws-cdk/region-info/test/fact.test.ts index 5522d4bd16d1d..b3e3bab7fdab7 100644 --- a/packages/@aws-cdk/region-info/test/fact.test.ts +++ b/packages/@aws-cdk/region-info/test/fact.test.ts @@ -1,5 +1,5 @@ -import { AWS_REGIONS } from '../build-tools/aws-entities'; import { Fact, FactName } from '../lib'; +import { AWS_REGIONS } from '../lib/aws-entities'; describe('find', () => { test('returns undefined for an unknown fact', () => { diff --git a/packages/@aws-cdk/region-info/test/region-info.test.ts b/packages/@aws-cdk/region-info/test/region-info.test.ts index 984d5e31cf45d..a47e8829ad9f5 100644 --- a/packages/@aws-cdk/region-info/test/region-info.test.ts +++ b/packages/@aws-cdk/region-info/test/region-info.test.ts @@ -1,6 +1,6 @@ -import { AWS_REGIONS, AWS_SERVICES } from '../build-tools/aws-entities'; import { CLOUDWATCH_LAMBDA_INSIGHTS_ARNS } from '../build-tools/fact-tables'; -import { RegionInfo } from '../lib'; +import { FactName, RegionInfo } from '../lib'; +import { AWS_REGIONS, AWS_SERVICES } from '../lib/aws-entities'; test('built-in data is correct', () => { const snapshot: any = {}; @@ -43,3 +43,14 @@ test('built-in data features known regions', () => { expect(regions.map(region => region.name)).toContain(expected); } }); + +test('limitedRegionMap only returns information for certain regions', () => { + + const map = RegionInfo.limitedRegionMap(FactName.ELBV2_ACCOUNT, ['aws']); + expect(map['us-east-1']).toBeDefined(); + expect(map['cn-north-1']).not.toBeDefined(); + + const map2 = RegionInfo.limitedRegionMap(FactName.ELBV2_ACCOUNT, ['aws-cn']); + expect(map2['us-east-1']).not.toBeDefined(); + expect(map2['cn-north-1']).toBeDefined(); +}); diff --git a/packages/aws-cdk/lib/init.ts b/packages/aws-cdk/lib/init.ts index 1165018a9b469..6570931c12c68 100644 --- a/packages/aws-cdk/lib/init.ts +++ b/packages/aws-cdk/lib/init.ts @@ -191,6 +191,7 @@ export class InitTemplate { config.context = { ...config.context, ...futureFlags, + ...cxapi.NEW_PROJECT_DEFAULT_CONTEXT, }; await fs.writeJson(cdkJson, config, { spaces: 2 }); diff --git a/packages/aws-cdk/test/init.test.ts b/packages/aws-cdk/test/init.test.ts index fe27f114c12c3..bf06892e83b6a 100644 --- a/packages/aws-cdk/test/init.test.ts +++ b/packages/aws-cdk/test/init.test.ts @@ -81,9 +81,10 @@ describe.each(['1', '2'])('v%s tests', (majorVersion) => { const config = await fs.readJson(path.join(tmpDir, 'cdk.json')); const context = config.context || {}; - for (const [key, expected] of Object.entries(context)) { - const actual = cxapi.FUTURE_FLAGS[key]; - expect(actual).toEqual(expected); + for (const [key, actual] of Object.entries(context)) { + expect(key in cxapi.FUTURE_FLAGS || key in cxapi.NEW_PROJECT_DEFAULT_CONTEXT).toBeTruthy(); + + expect(cxapi.FUTURE_FLAGS[key] ?? cxapi.NEW_PROJECT_DEFAULT_CONTEXT[key]).toEqual(actual); } // assert that expired future flags are not part of the cdk.json diff --git a/tools/@aws-cdk/cdk-integ-tools/lib/integ-helpers.ts b/tools/@aws-cdk/cdk-integ-tools/lib/integ-helpers.ts index 6b725d980a6f6..775ed5202d405 100644 --- a/tools/@aws-cdk/cdk-integ-tools/lib/integ-helpers.ts +++ b/tools/@aws-cdk/cdk-integ-tools/lib/integ-helpers.ts @@ -1,7 +1,7 @@ // Helper functions for integration tests import { spawnSync } from 'child_process'; import * as path from 'path'; -import { AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY, FUTURE_FLAGS } from '@aws-cdk/cx-api'; +import { AVAILABILITY_ZONE_FALLBACK_CONTEXT_KEY, FUTURE_FLAGS, TARGET_PARTITIONS } from '@aws-cdk/cx-api'; import * as fs from 'fs-extra'; const CDK_OUTDIR = 'cdk-integ.out'; @@ -353,6 +353,12 @@ export const DEFAULT_SYNTH_OPTIONS = { }, // Enable feature flags for all integ tests ...FUTURE_FLAGS, + + // Restricting to these target partitions makes most service principals synthesize to + // `service.${URL_SUFFIX}`, which is technically *incorrect* (it's only `amazonaws.com` + // or `amazonaws.com.cn`, never UrlSuffix for any of the restricted regions) but it's what + // most existing integ tests contain, and we want to disturb as few as possible. + [TARGET_PARTITIONS]: ['aws', 'aws-cn'], }, env: { CDK_INTEG_ACCOUNT: '12345678',