Skip to content

Commit 4e97389

Browse files
authored
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 96da1cd commit 4e97389

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';
@@ -350,19 +345,10 @@ class FunctionGenerator implements ConstructContainerEntryGenerator {
350345
scope,
351346
backendSecretResolver,
352347
}: GenerateContainerEntryProps) => {
353-
// resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope.
354-
const resolvedLayers = Object.entries(this.props.layers).map(([key, arn]) =>
355-
LayerVersion.fromLayerVersionArn(
356-
scope,
357-
`${this.props.name}-${key}-layer`,
358-
arn
359-
)
360-
);
361-
362348
return new AmplifyFunction(
363349
scope,
364350
this.props.name,
365-
{ ...this.props, resolvedLayers },
351+
this.props,
366352
backendSecretResolver,
367353
this.outputStorageStrategy
368354
);
@@ -382,14 +368,39 @@ class AmplifyFunction
382368
constructor(
383369
scope: Construct,
384370
id: string,
385-
props: HydratedFunctionProps & { resolvedLayers: ILayerVersion[] },
371+
props: HydratedFunctionProps,
386372
backendSecretResolver: BackendSecretResolver,
387373
outputStorageStrategy: BackendOutputStorageStrategy<FunctionOutput>
388374
) {
389375
super(scope, id);
390376

391377
this.stack = Stack.of(scope);
392378

379+
// resolve layers to LayerVersion objects for the NodejsFunction constructor using the scope.
380+
const resolvedLayers = Object.entries(props.layers).map(([key, arn]) => {
381+
const layerRegion = arn.split(':')[3];
382+
// If region is an unresolved token, use lazy to get region
383+
const region = Token.isUnresolved(this.stack.region)
384+
? Lazy.string({
385+
produce: () => this.stack.region,
386+
})
387+
: this.stack.region;
388+
389+
if (layerRegion !== region) {
390+
throw new AmplifyUserError('InvalidLayerArnRegionError', {
391+
message: `Region in ARN does not match function region for layer: ${key}`,
392+
resolution:
393+
'Update the layer ARN with the same region as the function',
394+
});
395+
}
396+
397+
return LayerVersion.fromLayerVersionArn(
398+
scope,
399+
`${props.name}-${key}-layer`,
400+
arn
401+
);
402+
});
403+
393404
const runtime = nodeVersionMap[props.runtime];
394405

395406
const require = createRequire(import.meta.url);
@@ -432,7 +443,7 @@ class AmplifyFunction
432443
timeout: Duration.seconds(props.timeoutSeconds),
433444
memorySize: props.memoryMB,
434445
runtime: nodeVersionMap[props.runtime],
435-
layers: props.resolvedLayers,
446+
layers: resolvedLayers,
436447
bundling: {
437448
...props.bundling,
438449
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)