@@ -110,16 +110,12 @@ export class SamFramework implements IFramework {
110110
111111 const lambdas : any [ ] = [ ] ;
112112
113- // get all resources of type AWS::Serverless::Function
114- for ( const resourceName in template . Resources ) {
115- const resource = template . Resources [ resourceName ] ;
116- if ( resource . Type === 'AWS::Serverless::Function' ) {
117- lambdas . push ( {
118- Name : resourceName ,
119- ...resource ,
120- } ) ;
121- }
122- }
113+ // Recursively parse templates to find all Lambda functions, including those in nested stacks
114+ await this . parseLambdasFromTemplate (
115+ template ,
116+ path . dirname ( path . resolve ( samTemplateFile ) ) ,
117+ lambdas ,
118+ ) ;
123119
124120 const lambdasDiscovered : LambdaResource [ ] = [ ] ;
125121
@@ -130,11 +126,19 @@ export class SamFramework implements IFramework {
130126 awsConfiguration ,
131127 ) ;
132128
129+ const stackAndNestedStackNames = [
130+ ...new Set ( lambdasInStack . map ( ( l ) => l . stackName ) ) ,
131+ ] ;
132+
133133 Logger . verbose (
134134 `[SAM] Found Lambdas in stack ${ stackName } :` ,
135135 JSON . stringify ( lambdasInStack , null , 2 ) ,
136136 ) ;
137137
138+ Logger . verbose (
139+ `[SAM] Found the following stacks and nested stacks: ${ stackAndNestedStackNames . join ( ', ' ) } ` ,
140+ ) ;
141+
138142 // get tags for each Lambda
139143 for ( const func of lambdas ) {
140144 const handlerFull = path . join (
@@ -221,6 +225,65 @@ export class SamFramework implements IFramework {
221225
222226 return lambdasDiscovered ;
223227 }
228+
229+ /**
230+ * Recursively parse templates to find all Lambda functions, including nested stacks
231+ * @param template The parsed CloudFormation/SAM template
232+ * @param templateDir The directory containing the template file
233+ * @param lambdas The array to collect Lambda functions into
234+ */
235+ private async parseLambdasFromTemplate (
236+ template : any ,
237+ templateDir : string ,
238+ lambdas : any [ ] ,
239+ ) : Promise < void > {
240+ if ( ! template . Resources ) {
241+ return ;
242+ }
243+
244+ for ( const resourceName in template . Resources ) {
245+ const resource = template . Resources [ resourceName ] ;
246+
247+ // Check if it's a Lambda function
248+ if ( resource . Type === 'AWS::Serverless::Function' ) {
249+ lambdas . push ( {
250+ Name : resourceName ,
251+ ...resource ,
252+ } ) ;
253+ }
254+ // Check if it's a nested stack
255+ else if (
256+ resource . Type === 'AWS::Serverless::Application' ||
257+ resource . Type === 'AWS::CloudFormation::Stack'
258+ ) {
259+ const nestedTemplateLocation = resource . Properties ?. Location ;
260+ if ( nestedTemplateLocation ) {
261+ try {
262+ const nestedTemplatePath = path . resolve (
263+ templateDir ,
264+ nestedTemplateLocation ,
265+ ) ;
266+ const nestedTemplateContent = await fs . readFile (
267+ nestedTemplatePath ,
268+ 'utf-8' ,
269+ ) ;
270+ const nestedTemplate = yaml . parse ( nestedTemplateContent ) ;
271+
272+ // Recursively parse the nested template
273+ await this . parseLambdasFromTemplate (
274+ nestedTemplate ,
275+ path . dirname ( nestedTemplatePath ) ,
276+ lambdas ,
277+ ) ;
278+ } catch ( err : any ) {
279+ Logger . warn (
280+ `[SAM] Could not parse nested template at ${ nestedTemplateLocation } : ${ err . message } ` ,
281+ ) ;
282+ }
283+ }
284+ }
285+ }
286+ }
224287}
225288
226289export const samFramework = new SamFramework ( ) ;
0 commit comments