@@ -6,16 +6,15 @@ import {
6
6
PublishLayerVersionCommand ,
7
7
UpdateFunctionConfigurationCommand ,
8
8
GetFunctionCommand ,
9
+ ListLayersCommand ,
9
10
} from "@aws-sdk/client-lambda" ;
10
11
import {
11
12
IAMClient ,
12
- CreatePolicyCommand ,
13
- AttachRolePolicyCommand ,
14
- GetPolicyCommand ,
13
+ GetRolePolicyCommand ,
14
+ PutRolePolicyCommand ,
15
15
} from "@aws-sdk/client-iam" ;
16
16
import { getDebuggerId , getVersion , moduleDirname } from "./lldebugger.js" ;
17
- import { Policy } from "@aws-sdk/client-iam" ;
18
- import { STSClient , GetCallerIdentityCommand } from "@aws-sdk/client-sts" ;
17
+ import { STSClient } from "@aws-sdk/client-sts" ;
19
18
import * as Functions from "./functions.js" ;
20
19
import fs from "fs/promises" ;
21
20
import * as path from "path" ;
@@ -24,6 +23,19 @@ const lambdaClient = new LambdaClient({});
24
23
const stsClient = new STSClient ( { } ) ;
25
24
const iamClient = new IAMClient ( { } ) ;
26
25
26
+ const inlinePolicyName = "LambdaLiveDebuggerPolicy" ;
27
+
28
+ const policyDocument = {
29
+ Version : "2012-10-17" ,
30
+ Statement : [
31
+ {
32
+ Action : "iot:*" ,
33
+ Resource : "*" ,
34
+ Effect : "Allow" ,
35
+ } ,
36
+ ] ,
37
+ } ;
38
+
27
39
async function findExistingLayer ( layerName : string ) {
28
40
const listLayerVersionsCommand = new ListLayerVersionsCommand ( {
29
41
LayerName : layerName ,
@@ -37,7 +49,7 @@ async function findExistingLayer(layerName: string) {
37
49
return undefined ;
38
50
}
39
51
40
- async function deployLayer ( layerName : string ) {
52
+ async function deployLayer ( ) {
41
53
const layerDescription = `Lambda Live Debugger Layer version ${ await getVersion ( ) } ` ;
42
54
43
55
let layerZipPathFullPath = path . join (
@@ -103,6 +115,68 @@ async function deployLayer(layerName: string) {
103
115
return response . LayerVersionArn ;
104
116
}
105
117
118
+ async function deleteLayer ( ) {
119
+ let nextMarker : string | undefined ;
120
+ do {
121
+ const layers = await lambdaClient . send (
122
+ new ListLayersCommand ( {
123
+ Marker : nextMarker ,
124
+ MaxItems : 10 ,
125
+ } )
126
+ ) ;
127
+
128
+ // Filter layers by name
129
+ const targetLayers =
130
+ layers . Layers ?. filter ( ( layer ) => layer . LayerName === layerName ) || [ ] ;
131
+
132
+ for ( const layer of targetLayers ) {
133
+ await deleteAllVersionsOfLayer ( layer . LayerArn ! ) ;
134
+ }
135
+
136
+ nextMarker = layers . NextMarker ;
137
+ } while ( nextMarker ) ;
138
+ }
139
+
140
+ async function deleteAllVersionsOfLayer ( layerArn : string ) : Promise < void > {
141
+ let nextMarker : string | undefined ;
142
+ do {
143
+ const versions = await lambdaClient . send (
144
+ new ListLayerVersionsCommand ( {
145
+ LayerName : layerArn ,
146
+ Marker : nextMarker ,
147
+ //MaxItems: 5,
148
+ } )
149
+ ) ;
150
+
151
+ for ( const version of versions . LayerVersions || [ ] ) {
152
+ await deleteLayerVersion ( layerArn , version . Version ! ) ;
153
+ }
154
+
155
+ nextMarker = versions . NextMarker ;
156
+ } while ( nextMarker ) ;
157
+ }
158
+
159
+ async function deleteLayerVersion (
160
+ layerArn : string ,
161
+ versionNumber : number
162
+ ) : Promise < void > {
163
+ try {
164
+ await lambdaClient . send (
165
+ new DeleteLayerVersionCommand ( {
166
+ LayerName : layerArn ,
167
+ VersionNumber : versionNumber ,
168
+ } )
169
+ ) ;
170
+ console . log ( `Deleted version ${ versionNumber } of layer ${ layerArn } ` ) ;
171
+ } catch ( error ) {
172
+ console . error (
173
+ `Error deleting version ${ versionNumber } of layer ${ layerArn } :` ,
174
+ error
175
+ ) ;
176
+ throw error ;
177
+ }
178
+ }
179
+
106
180
async function attachLayerToFunction (
107
181
functionName : string ,
108
182
functionId : string ,
@@ -143,75 +217,7 @@ async function attachLayerToFunction(
143
217
console . log ( "Function configuration updated" ) ;
144
218
}
145
219
146
- async function findOrCreatePolicy ( ) : Promise < string > {
147
- const version = await getVersion ( ) ;
148
- const policyName = `lambda-live-debugger-policy-v${ version . replace (
149
- / \. / g,
150
- "-"
151
- ) } `;
152
-
153
- let existingPolicy : Policy | undefined ;
154
-
155
- try {
156
- // get account number
157
- const { Account : account } = await stsClient . send (
158
- new GetCallerIdentityCommand ( { } )
159
- ) ;
160
-
161
- const response = await iamClient . send (
162
- new GetPolicyCommand ( {
163
- PolicyArn : `arn:aws:iam::${ account } :policy/${ policyName } ` ,
164
- } )
165
- ) ;
166
- existingPolicy = response . Policy ;
167
- } catch ( e : any ) {
168
- if ( e . name === "NoSuchEntityException" ) {
169
- // did not find the policy
170
- } else {
171
- throw e ;
172
- }
173
- }
174
-
175
- if ( existingPolicy ) {
176
- if ( ! existingPolicy . Arn ) {
177
- throw new Error (
178
- "Failed to retrieve the policy ARN of the existing policy"
179
- ) ;
180
- }
181
- return existingPolicy . Arn ;
182
- }
183
-
184
- const policyDocument = {
185
- Version : "2012-10-17" ,
186
- Statement : [
187
- {
188
- Action : "iot:*" ,
189
- Resource : "*" ,
190
- Effect : "Allow" ,
191
- } ,
192
- ] ,
193
- } ;
194
-
195
- // If not found, create a new policy
196
- const createPolicyResponse = await iamClient . send (
197
- new CreatePolicyCommand ( {
198
- PolicyName : policyName ,
199
- PolicyDocument : JSON . stringify ( policyDocument ) ,
200
- } )
201
- ) ;
202
- console . log ( `Policy ${ policyName } created` ) ;
203
-
204
- if ( ! createPolicyResponse . Policy ?. Arn ) {
205
- throw new Error ( "Failed to retrieve the policy ARN of the new policy" ) ;
206
- }
207
-
208
- return createPolicyResponse . Policy ?. Arn ;
209
- }
210
-
211
- async function attachPolicyToLambdaRole (
212
- functionName : string ,
213
- policyArn : string
214
- ) {
220
+ async function addPolicyToLambdaRole ( functionName : string ) {
215
221
// Retrieve the Lambda function's execution role ARN
216
222
const getFunctionResponse = await lambdaClient . send (
217
223
new GetFunctionCommand ( {
@@ -220,44 +226,78 @@ async function attachPolicyToLambdaRole(
220
226
) ;
221
227
const roleArn = getFunctionResponse . Configuration ?. Role ;
222
228
if ( ! roleArn ) {
223
- console . error ( "Failed to retrieve function role ARN" ) ;
224
- return ;
229
+ throw new Error (
230
+ `Failed to retrieve the role ARN for function ${ functionName } `
231
+ ) ;
225
232
}
226
233
227
234
// Extract the role name from the role ARN
228
235
const roleName = roleArn . split ( "/" ) . pop ( ) ;
229
236
230
- if ( ! policyArn || ! roleName ) {
231
- console . error ( "Failed to proceed without policy ARN or role name" ) ;
232
- return ;
237
+ if ( ! roleName ) {
238
+ throw new Error (
239
+ `Failed to extract role name from role ARN: ${ roleArn } for function ${ functionName } `
240
+ ) ;
233
241
}
234
242
235
- // Attach the policy to the role
236
- await iamClient . send (
237
- new AttachRolePolicyCommand ( {
238
- RoleName : roleName ,
239
- PolicyArn : policyArn ,
240
- } )
241
- ) ;
242
- console . log ( `Policy attached to Lambda role` ) ;
243
+ const existingPolicy = getPolicyDocument ( roleName ) ;
244
+
245
+ let addPolicy : boolean = true ;
246
+
247
+ // compare existing policy with the new one
248
+ if ( existingPolicy ) {
249
+ if ( JSON . stringify ( existingPolicy ) === JSON . stringify ( policyDocument ) ) {
250
+ console . log (
251
+ `Correct policy already attached to the role ${ roleName } for function ${ functionName } `
252
+ ) ;
253
+ addPolicy = false ;
254
+ }
255
+ }
256
+
257
+ if ( addPolicy ) {
258
+ // add inline policy to the role using PutRolePolicyCommand
259
+ await iamClient . send (
260
+ new PutRolePolicyCommand ( {
261
+ RoleName : roleName ,
262
+ PolicyName : inlinePolicyName ,
263
+ PolicyDocument : JSON . stringify ( policyDocument ) ,
264
+ } )
265
+ ) ;
266
+ }
267
+ }
268
+
269
+ async function getPolicyDocument ( roleName : string ) {
270
+ try {
271
+ const policy = await iamClient . send (
272
+ new GetRolePolicyCommand ( {
273
+ RoleName : roleName ,
274
+ PolicyName : inlinePolicyName ,
275
+ } )
276
+ ) ;
277
+
278
+ return policy . PolicyDocument ;
279
+ } catch ( error ) {
280
+ // console.error(
281
+ // `Error retrieving policy document for ${inlinePolicyName}:`,
282
+ // error
283
+ // );
284
+ }
243
285
}
244
286
245
287
const layerName = "LambdaLiveDebugger" ; // Replace with your layer name
246
288
247
289
export async function deployInfrastructure ( ) {
248
- const layerVersionArnPromise = deployLayer ( layerName ) ;
249
- const policyArnPromise = findOrCreatePolicy ( ) ;
290
+ const layerVersionArnPromise = deployLayer ( ) ;
250
291
251
292
const layerVersionArn = await layerVersionArnPromise ;
252
- const policyArn = await policyArnPromise ;
253
293
254
294
const promises : Promise < void > [ ] = [ ] ;
255
295
256
296
for ( const func of Functions . getFunctions ( ) ) {
257
297
promises . push (
258
298
attachLayerToFunction ( func . functionName , func . functionId , layerVersionArn )
259
299
) ;
260
- promises . push ( attachPolicyToLambdaRole ( func . functionName , policyArn ) ) ;
300
+ promises . push ( addPolicyToLambdaRole ( func . functionName ) ) ;
261
301
}
262
302
263
303
await Promise . all ( promises ) ;
0 commit comments