diff --git a/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.test.ts b/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.test.ts index a8a9d8610ff..001eecef4c7 100644 --- a/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.test.ts +++ b/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.test.ts @@ -199,6 +199,28 @@ describe('CFNOutputResolver', () => { TopicName: 'snsTopic', }, }, + CustomS3AutoDeleteObjectsCustomResourceProviderHandler: { + Type: 'AWS::Lambda::Function', + Properties: { + FunctionName: 'CustomS3AutoDeleteObjectsCustomResourceProviderHandler', + Handler: 'index.handler', + Code: { + ZipFile: 'exports.handler = function() {}', + }, + Runtime: 'nodejs14.x', + }, + }, + CustomS3AutoDeleteObjects: { + Type: 'Custom::S3AutoDeleteObjects', + Properties: { + ServiceToken: { + 'Fn::GetAtt': ['CustomS3AutoDeleteObjectsCustomResourceProviderHandler', 'Arn'], + }, + BucketName: { + Ref: 'MyS3Bucket', + }, + }, + }, }, }; const expectedTemplate: CFNTemplate = { @@ -372,8 +394,27 @@ describe('CFNOutputResolver', () => { TopicName: 'snsTopic', }, }, + CustomS3AutoDeleteObjectsCustomResourceProviderHandler: { + Type: 'AWS::Lambda::Function', + Properties: { + FunctionName: 'CustomS3AutoDeleteObjectsCustomResourceProviderHandler', + Handler: 'index.handler', + Code: { + ZipFile: 'exports.handler = function() {}', + }, + Runtime: 'nodejs14.x', + }, + }, + CustomS3AutoDeleteObjects: { + Type: 'Custom::S3AutoDeleteObjects', + Properties: { + ServiceToken: 'arn:aws:lambda:us-east-1:12345:function:mycustomS3AutoDeleteObjectsLambdaFunction', + BucketName: 'test-bucket', + }, + }, }, }; + it('should resolve output references', () => { expect( new CfnOutputResolver(template, 'us-east-1', '12345').resolve( @@ -431,6 +472,15 @@ describe('CFNOutputResolver', () => { Timestamp: new Date('2025-04-02T22:27:41.603000+00:00'), ResourceStatus: 'CREATE_COMPLETE', }, + { + StackName: 'amplify-amplifycodegen-dev', + StackId: 'arn:aws:cloudformation:us-west-2:123456789:stack/amplify-amplifycodegen-dev', + LogicalResourceId: 'CustomS3AutoDeleteObjectsCustomResourceProviderHandler', + PhysicalResourceId: 'mycustomS3AutoDeleteObjectsLambdaFunction', + ResourceType: 'AWS::Lambda::Function', + Timestamp: new Date('2025-04-02T22:27:41.603000+00:00'), + ResourceStatus: 'CREATE_COMPLETE', + }, ], ), ).toEqual(expectedTemplate); diff --git a/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.ts b/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.ts index b15e7760b0d..b607806da0b 100644 --- a/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.ts +++ b/packages/amplify-migration-template-gen/src/resolvers/cfn-output-resolver.ts @@ -157,6 +157,10 @@ class CfnOutputResolver { return { Arn: `arn:aws:sqs:${this.region}:${this.accountId}:${resourceIdentifier}`, }; + case 'AWS::Lambda::Function': + return { + Arn: `arn:aws:lambda:${this.region}:${this.accountId}:function:${resourceIdentifier}`, + }; default: return undefined; } diff --git a/packages/amplify-migration-template-gen/src/template-generator.test.ts b/packages/amplify-migration-template-gen/src/template-generator.test.ts index ac36e6748eb..f13aac652f8 100644 --- a/packages/amplify-migration-template-gen/src/template-generator.test.ts +++ b/packages/amplify-migration-template-gen/src/template-generator.test.ts @@ -834,7 +834,7 @@ describe('TemplateGenerator', () => { CFN_AUTH_TYPE.IdentityPoolRoleAttachment, CFN_AUTH_TYPE.UserPoolDomain, ], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 2, @@ -848,7 +848,7 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_AUTH_TYPE.UserPoolGroup], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 3, @@ -862,7 +862,7 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_S3_TYPE.Bucket], - expect.any(Function), + undefined, ); } @@ -892,7 +892,7 @@ describe('TemplateGenerator', () => { CFN_AUTH_TYPE.IdentityPoolRoleAttachment, CFN_AUTH_TYPE.UserPoolDomain, ], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 2, @@ -906,7 +906,7 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_AUTH_TYPE.UserPoolGroup], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 3, @@ -920,7 +920,7 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_S3_TYPE.Bucket], - expect.any(Function), + undefined, ); } @@ -950,7 +950,7 @@ describe('TemplateGenerator', () => { CFN_AUTH_TYPE.IdentityPoolRoleAttachment, CFN_AUTH_TYPE.UserPoolDomain, ], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 2, @@ -964,7 +964,7 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_AUTH_TYPE.UserPoolGroup], - expect.any(Function), + undefined, ); expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 3, @@ -978,8 +978,9 @@ describe('TemplateGenerator', () => { APP_ID, ENV_NAME, [CFN_S3_TYPE.Bucket], - expect.any(Function), + undefined, ); + // custom resource category expect(CategoryTemplateGenerator).toHaveBeenNthCalledWith( 4, GEN1_ROOT_STACK_NAME, diff --git a/packages/amplify-migration-template-gen/src/template-generator.ts b/packages/amplify-migration-template-gen/src/template-generator.ts index 23599301ef7..ca5141781d8 100644 --- a/packages/amplify-migration-template-gen/src/template-generator.ts +++ b/packages/amplify-migration-template-gen/src/template-generator.ts @@ -260,13 +260,12 @@ class TemplateGenerator { let updatingGen1CategoryStack; try { const { newTemplate, parameters: gen1StackParameters } = await categoryTemplateGenerator.generateGen1PreProcessTemplate(); - assert(gen1StackParameters); updatingGen1CategoryStack = ora(`Updating Gen 1 ${this.getStackCategoryName(category)} stack...`).start(); const gen1StackUpdateStatus = await tryUpdateStack(this.cfnClient, sourceCategoryStackId, gen1StackParameters, newTemplate); - assert(gen1StackUpdateStatus === CFNStackStatus.UPDATE_COMPLETE); + assert(gen1StackUpdateStatus === CFNStackStatus.UPDATE_COMPLETE, `Gen 1 stack is in an invalid state: ${gen1StackUpdateStatus}`); updatingGen1CategoryStack.succeed(`Updated Gen 1 ${this.getStackCategoryName(category)} stack successfully`); return newTemplate; @@ -297,7 +296,7 @@ class TemplateGenerator { const gen2StackUpdateStatus = await tryUpdateStack(this.cfnClient, destinationCategoryStackId, parameters ?? [], newTemplate); - assert(gen2StackUpdateStatus === CFNStackStatus.UPDATE_COMPLETE); + assert(gen2StackUpdateStatus === CFNStackStatus.UPDATE_COMPLETE, `Gen 2 stack is in an invalid state: ${gen2StackUpdateStatus}`); updatingGen2CategoryStack.succeed(`Updated Gen 2 ${this.getStackCategoryName(category)} stack successfully`); return { newTemplate, oldTemplate, parameters }; @@ -325,7 +324,7 @@ class TemplateGenerator { destinationStackId, this.createCategoryTemplateGenerator(sourceStackId, destinationStackId, config.resourcesToRefactor), ]); - } else if (customResourceMap && !Object.values(NON_CUSTOM_RESOURCE_CATEGORY).includes(category as NON_CUSTOM_RESOURCE_CATEGORY)) { + } else if (customResourceMap && this.isCustomResource(category)) { this.categoryTemplateGenerators.push([ category, sourceStackId, @@ -354,17 +353,19 @@ class TemplateGenerator { this.appId, this.environmentName, resourcesToRefactor, - (_resourcesToMove: CFN_CATEGORY_TYPE[], cfnResource: [string, CFNResource]) => { - const [logicalId] = cfnResource; - - // Check if customResourceMap contains the logical ID - return ( - customResourceMap?.some( - (resourceMapping) => - resourceMapping.Source.LogicalResourceId === logicalId || resourceMapping.Destination.LogicalResourceId === logicalId, - ) ?? false - ); - }, + customResourceMap + ? (_resourcesToMove: CFN_CATEGORY_TYPE[], cfnResource: [string, CFNResource]) => { + const [logicalId] = cfnResource; + + // Check if customResourceMap contains the logical ID + return ( + customResourceMap?.some( + (resourceMapping) => + resourceMapping.Source.LogicalResourceId === logicalId || resourceMapping.Destination.LogicalResourceId === logicalId, + ) ?? false + ); + } + : undefined, ); } diff --git a/packages/amplify-migration-template-gen/src/types.ts b/packages/amplify-migration-template-gen/src/types.ts index 0480162f1bc..2de6f6a7866 100644 --- a/packages/amplify-migration-template-gen/src/types.ts +++ b/packages/amplify-migration-template-gen/src/types.ts @@ -109,7 +109,11 @@ export enum CFN_SQS_TYPE { Queue = 'AWS::SQS::Queue', } -export type CFN_RESOURCE_TYPES = CFN_AUTH_TYPE | CFN_S3_TYPE | CFN_IAM_TYPE | CFN_SQS_TYPE; +export enum CFN_LAMBDA_TYPE { + Function = 'AWS::Lambda::Function', +} + +export type CFN_RESOURCE_TYPES = CFN_AUTH_TYPE | CFN_S3_TYPE | CFN_IAM_TYPE | CFN_SQS_TYPE | CFN_LAMBDA_TYPE; export type AWS_RESOURCE_ATTRIBUTES = 'Arn';