Skip to content

Commit 94103c3

Browse files
rtpascualstocaaro
authored andcommitted
add validation if layer arn region does not match function region (#2188)
* add validation if layer arn region does not match function region * add backend to changeset * update function region in test and add conditional logic for region
1 parent 125d43e commit 94103c3

File tree

4 files changed

+70
-20
lines changed

4 files changed

+70
-20
lines changed

.changeset/tiny-cameras-happen.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@aws-amplify/backend': patch
3+
'@aws-amplify/backend-function': patch
4+
---
5+
6+
add validation if layer arn region does not match function region

packages/backend-function/src/factory.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { NodeVersion, defineFunction } from './factory.js';
1717
import { lambdaWithDependencies } from './test-assets/lambda-with-dependencies/resource.js';
1818
import { Runtime } from 'aws-cdk-lib/aws-lambda';
1919
import { Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam';
20+
import { AmplifyUserError } from '@aws-amplify/platform-core';
2021

2122
const createStackAndSetContext = (): Stack => {
2223
const app = new App();
@@ -411,6 +412,38 @@ void describe('AmplifyFunctionFactory', () => {
411412
});
412413
});
413414

415+
void describe('layers property', () => {
416+
void it('defaults to no layers', () => {
417+
const lambda = defineFunction({
418+
entry: './test-assets/default-lambda/handler.ts',
419+
}).getInstance(getInstanceProps);
420+
const template = Template.fromStack(lambda.stack);
421+
422+
template.resourceCountIs('AWS::Lambda::LayerVersion', 0);
423+
});
424+
425+
void it('throws if layer arn region is not the same as function region', () => {
426+
assert.throws(
427+
() =>
428+
defineFunction({
429+
entry: './test-assets/default-lambda/handler.ts',
430+
layers: {
431+
layer1:
432+
'arn:aws:lambda:some-region:123456789012:layer:my-layer-1:1',
433+
},
434+
}).getInstance(getInstanceProps),
435+
(error: AmplifyUserError) => {
436+
assert.strictEqual(
437+
error.message,
438+
'Region in ARN does not match function region for layer: layer1'
439+
);
440+
assert.ok(error.resolution);
441+
return true;
442+
}
443+
);
444+
});
445+
});
446+
414447
void describe('minify property', () => {
415448
void it('sets minify to false', () => {
416449
const lambda = defineFunction({

packages/backend-function/src/factory.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,11 @@ import {
2323
SsmEnvironmentEntry,
2424
StackProvider,
2525
} from '@aws-amplify/plugin-types';
26-
import { Duration, Stack, Tags } from 'aws-cdk-lib';
26+
import { Duration, Lazy, Stack, Tags, Token } from 'aws-cdk-lib';
2727
import { Rule } from 'aws-cdk-lib/aws-events';
2828
import * as targets from 'aws-cdk-lib/aws-events-targets';
2929
import { Policy } from 'aws-cdk-lib/aws-iam';
30-
import {
31-
CfnFunction,
32-
ILayerVersion,
33-
LayerVersion,
34-
Runtime,
35-
} from 'aws-cdk-lib/aws-lambda';
30+
import { CfnFunction, LayerVersion, Runtime } from 'aws-cdk-lib/aws-lambda';
3631
import { NodejsFunction, OutputFormat } from 'aws-cdk-lib/aws-lambda-nodejs';
3732
import { Construct } from 'constructs';
3833
import { readFileSync } from 'fs';
@@ -361,19 +356,10 @@ class FunctionGenerator implements ConstructContainerEntryGenerator {
361356
scope,
362357
backendSecretResolver,
363358
}: GenerateContainerEntryProps) => {
364-
// resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope.
365-
const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) =>
366-
LayerVersion.fromLayerVersionArn(
367-
scope,
368-
`${this.props.name}-${key}-layer`,
369-
arn
370-
)
371-
);
372-
373359
return new AmplifyFunction(
374360
scope,
375361
this.props.name,
376-
{ ...this.props, resolvedLayers },
362+
this.props,
377363
backendSecretResolver,
378364
this.outputStorageStrategy
379365
);
@@ -393,14 +379,39 @@ class AmplifyFunction
393379
constructor(
394380
scope: Construct,
395381
id: string,
396-
props: HydratedFunctionProps & { resolvedLayers: ILayerVersion[] },
382+
props: HydratedFunctionProps,
397383
backendSecretResolver: BackendSecretResolver,
398384
outputStorageStrategy: BackendOutputStorageStrategy<FunctionOutput>
399385
) {
400386
super(scope, id);
401387

402388
this.stack = Stack.of(scope);
403389

390+
// resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope.
391+
const resolvedLayers = Object.entries(props.layers).map(([key, arn]) => {
392+
const layerRegion = arn.split(':')[3];
393+
// If region is an unresolved token, use lazy to get region
394+
const region = Token.isUnresolved(this.stack.region)
395+
? Lazy.string({
396+
produce: () => this.stack.region,
397+
})
398+
: this.stack.region;
399+
400+
if (layerRegion !== region) {
401+
throw new AmplifyUserError('InvalidLayerArnRegionError', {
402+
message: `Region in ARN does not match function region for layer: ${key}`,
403+
resolution:
404+
'Update the layer ARN with the same region as the function',
405+
});
406+
}
407+
408+
return LayerVersion.fromLayerVersionArn(
409+
scope,
410+
`${props.name}-${key}-layer`,
411+
arn
412+
);
413+
});
414+
404415
const runtime = nodeVersionMap[props.runtime];
405416

406417
const require = createRequire(import.meta.url);
@@ -449,7 +460,7 @@ class AmplifyFunction
449460
timeout: Duration.seconds(props.timeoutSeconds),
450461
memorySize: props.memoryMB,
451462
runtime: nodeVersionMap[props.runtime],
452-
layers: props.resolvedLayers,
463+
layers: resolvedLayers,
453464
bundling: {
454465
...props.bundling,
455466
banner: bannerCode,

packages/backend-function/src/layer_parser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const createStackAndSetContext = (): Stack => {
2020
app.node.setContext('amplify-backend-name', 'testEnvName');
2121
app.node.setContext('amplify-backend-namespace', 'testBackendId');
2222
app.node.setContext('amplify-backend-type', 'branch');
23-
const stack = new Stack(app);
23+
const stack = new Stack(app, 'Stack', { env: { region: 'us-east-1' } });
2424
return stack;
2525
};
2626

0 commit comments

Comments
 (0)