@@ -7,9 +7,11 @@ import {
77 BackendSecretResolver ,
88 ResolvePathResult ,
99} from '@aws-amplify/plugin-types' ;
10- import { SecretValue } from 'aws-cdk-lib' ;
10+ import { App , SecretValue , Stack } from 'aws-cdk-lib' ;
1111import assert from 'node:assert' ;
1212import { ParameterPathConversions } from '@aws-amplify/platform-core' ;
13+ import { Code , Function , Runtime } from 'aws-cdk-lib/aws-lambda' ;
14+ import { Template } from 'aws-cdk-lib/assertions' ;
1315
1416const testStack = { } as Construct ;
1517
@@ -47,23 +49,31 @@ class TestBackendSecret implements BackendSecret {
4749 } ;
4850}
4951
50- void describe ( 'functionEnvironmentTranslator ' , ( ) => {
52+ void describe ( 'FunctionEnvironmentTranslator ' , ( ) => {
5153 const backendResolver = new TestBackendSecretResolver ( ) ;
5254
5355 void it ( 'translates env props that do not contain secrets' , ( ) => {
5456 const functionEnvProp = {
5557 TEST_VAR : 'testValue' ,
5658 } ;
5759
58- const functionEnvironmentTranslator = new FunctionEnvironmentTranslator (
59- testStack ,
60+ const testLambda = getTestLambda ( ) ;
61+
62+ new FunctionEnvironmentTranslator (
63+ testLambda ,
6064 functionEnvProp ,
6165 backendResolver
6266 ) ;
6367
64- assert . deepEqual ( functionEnvironmentTranslator . getEnvironmentRecord ( ) , {
65- AMPLIFY_SECRET_PATHS : '{}' ,
66- TEST_VAR : 'testValue' ,
68+ const template = Template . fromStack ( Stack . of ( testLambda ) ) ;
69+ template . resourceCountIs ( 'AWS::Lambda::Function' , 1 ) ;
70+ template . hasResourceProperties ( 'AWS::Lambda::Function' , {
71+ Environment : {
72+ Variables : {
73+ AMPLIFY_SSM_ENV_CONFIG : '{}' ,
74+ TEST_VAR : 'testValue' ,
75+ } ,
76+ } ,
6777 } ) ;
6878 } ) ;
6979
@@ -73,35 +83,141 @@ void describe('functionEnvironmentTranslator', () => {
7383 TEST_SECRET : new TestBackendSecret ( 'secretValue' ) ,
7484 } ;
7585
76- const functionEnvironmentTranslator = new FunctionEnvironmentTranslator (
77- testStack ,
86+ const testLambda = getTestLambda ( ) ;
87+
88+ new FunctionEnvironmentTranslator (
89+ testLambda ,
7890 functionEnvProp ,
7991 backendResolver
8092 ) ;
8193
82- assert . deepEqual ( functionEnvironmentTranslator . getEnvironmentRecord ( ) , {
83- AMPLIFY_SECRET_PATHS : JSON . stringify ( {
84- '/amplify/testBackendId/testBranchName-branch-e482a1c36f/secretValue' : {
85- name : 'TEST_SECRET' ,
86- sharedPath : '/amplify/shared/testBackendId/secretValue' ,
94+ const template = Template . fromStack ( Stack . of ( testLambda ) ) ;
95+
96+ template . resourceCountIs ( 'AWS::Lambda::Function' , 1 ) ;
97+ template . hasResourceProperties ( 'AWS::Lambda::Function' , {
98+ Environment : {
99+ Variables : {
100+ AMPLIFY_SSM_ENV_CONFIG : JSON . stringify ( {
101+ '/amplify/testBackendId/testBranchName-branch-e482a1c36f/secretValue' :
102+ {
103+ name : 'TEST_SECRET' ,
104+ sharedPath : '/amplify/shared/testBackendId/secretValue' ,
105+ } ,
106+ } ) ,
107+ TEST_SECRET : '<value will be resolved during runtime>' ,
108+ TEST_VAR : 'testValue' ,
87109 } ,
88- } ) ,
89- TEST_SECRET : '<value will be resolved during runtime>' ,
90- TEST_VAR : 'testValue' ,
110+ } ,
91111 } ) ;
92112 } ) ;
93113
94114 void it ( 'throws if function prop contains a reserved env name' , ( ) => {
95115 const functionEnvProp = {
96- AMPLIFY_SECRET_PATHS : 'test' ,
116+ AMPLIFY_SSM_ENV_CONFIG : 'test' ,
97117 } ;
118+
98119 assert . throws (
99120 ( ) =>
100121 new FunctionEnvironmentTranslator (
101- testStack ,
122+ getTestLambda ( ) ,
102123 functionEnvProp ,
103124 backendResolver
104125 )
105126 ) ;
106127 } ) ;
128+
129+ void it ( 'does not add SSM policy if no ssm paths are present' , ( ) => {
130+ const functionEnvProp = {
131+ TEST_VAR : 'testValue' ,
132+ } ;
133+
134+ const testLambda = getTestLambda ( ) ;
135+
136+ new FunctionEnvironmentTranslator (
137+ testLambda ,
138+ functionEnvProp ,
139+ backendResolver
140+ ) ;
141+
142+ const template = Template . fromStack ( Stack . of ( testLambda ) ) ;
143+ template . resourceCountIs ( 'AWS::IAM::Policy' , 0 ) ;
144+ } ) ;
145+
146+ void it ( 'grants SSM read permissions for secret paths' , ( ) => {
147+ const functionEnvProp = {
148+ TEST_VAR : 'testValue' ,
149+ TEST_SECRET : new TestBackendSecret ( 'secretValue' ) ,
150+ } ;
151+
152+ const testLambda = getTestLambda ( ) ;
153+
154+ new FunctionEnvironmentTranslator (
155+ testLambda ,
156+ functionEnvProp ,
157+ backendResolver
158+ ) ;
159+
160+ const template = Template . fromStack ( Stack . of ( testLambda ) ) ;
161+
162+ template . resourceCountIs ( 'AWS::IAM::Policy' , 1 ) ;
163+ template . hasResourceProperties ( 'AWS::IAM::Policy' , {
164+ PolicyDocument : {
165+ Statement : [
166+ {
167+ Effect : 'Allow' ,
168+ Action : 'ssm:GetParameters' ,
169+ Resource : [
170+ {
171+ 'Fn::Join' : [
172+ '' ,
173+ [
174+ 'arn:' ,
175+ {
176+ Ref : 'AWS::Partition' ,
177+ } ,
178+ ':ssm:' ,
179+ {
180+ Ref : 'AWS::Region' ,
181+ } ,
182+ ':' ,
183+ {
184+ Ref : 'AWS::AccountId' ,
185+ } ,
186+ ':parameter/amplify/testBackendId/testBranchName-branch-e482a1c36f/secretValue' ,
187+ ] ,
188+ ] ,
189+ } ,
190+ {
191+ 'Fn::Join' : [
192+ '' ,
193+ [
194+ 'arn:' ,
195+ {
196+ Ref : 'AWS::Partition' ,
197+ } ,
198+ ':ssm:' ,
199+ {
200+ Ref : 'AWS::Region' ,
201+ } ,
202+ ':' ,
203+ {
204+ Ref : 'AWS::AccountId' ,
205+ } ,
206+ ':parameter/amplify/shared/testBackendId/secretValue' ,
207+ ] ,
208+ ] ,
209+ } ,
210+ ] ,
211+ } ,
212+ ] ,
213+ } ,
214+ } ) ;
215+ } ) ;
107216} ) ;
217+
218+ const getTestLambda = ( ) =>
219+ new Function ( new Stack ( new App ( ) ) , 'testFunction' , {
220+ code : Code . fromInline ( 'test code' ) ,
221+ runtime : Runtime . NODEJS_20_X ,
222+ handler : 'handler' ,
223+ } ) ;
0 commit comments