response mapping template for a javascript resolver - graphql

I have resolvers written in JS and not VTL since AWS added support for it.
I am using AWS SAM template and this is how it looks
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
RequestMappingTemplate: |
{
"operation": "PutItem",
"key": util.dynamodb.toMapValues({"userId": ctx.userId, "sortKey": ctx.sortKey}),
"attributeValues": util.dynamodb.toMapValues(ctx),
}
ResponseMappingTemplate: "ctx.result"
But when I query the mutation on the Appsync console I get the following error
Unrecognized token 'util': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')\
I tried a couple of variations where I passed the entire value as string but it didn't work.
What am I missing or doing wrong in this template mapping?
Edit - My Updated Answer :
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
Code: |
import { util } from '#aws-appsync/utils';
export function request(ctx) {
return {
operation: 'PutItem',
key: util.dynamodb.toMapValues({"userId":
ctx.userId, "sortkey": ctx.sortKey}),
attributeValues: util.dynamodb.toMapValues(ctx),
}
}
export function response(ctx) {
const { error, result } = ctx;
if (error) {
return util.appendError(error.message, error.type, result);
}
return ctx.result;
}
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0

Think JS resolvers are only available in Pipeline Resolvers currently and not Unit Resolvers. Also, in a Pipeline Resolver you may need to add the runtime in someway that is similar to below. Without it it may be defaulting to VTL. Note, not tested this in SAM just guessing based off of Cloudformation and CDK docs.
AWS::AppSync::Resolver
AWS::AppSync::Resolver Runtime
MyResolver:
Type: AWS::AppSync::Resolver
DependsOn: AppSyncSchema
Properties:
ApiId: !GetAtt AppSyncApi.ApiId
TypeName: Mutation
FieldName: addUser
DataSourceName: !GetAtt UsersTableDataSource.Name
RequestMappingTemplate: |
{
"operation": "PutItem",
"key": util.dynamodb.toMapValues({"userId": ctx.noteId, "selection": sk}),
"attributeValues": util.dynamodb.toMapValues(ctx),
}
ResponseMappingTemplate: "ctx.result"
Runtime:
Name: APPSYNC_JS
RuntimeVersion: 1.0.0

Related

How can I troubleshoot an error: lib/graphql has no exported mutation - for a mutation I have defined and which appears in graphql.tsx

I'm trying to figure out what I need to do in order to have lib/graphql recognise the mutations I have made.
I have an issue.tsx (which is a form). It imports:
import {
IssueInput,
useUpdateIssueMutation,
useAllIssuesQuery,
useCreateIssueMutation,
useDeleteIssueMutation,
Issue as IssueGQLType,
} from "lib/graphql"
Other than IssueInput and Issue, I'm getting errors in my terminal that say these queries and mutations are not exported members.
However when I try to load the issue page in local host, I get an error that says:
error - GraphQLError [Object]: Syntax Error: Expected Name, found
. It points to the line where Issue is imported.
I made all of these queries and mutations in my resolver as follows:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { Issue } from "./issue.model"
import { IssueService } from "./issue.service"
import { IssueInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
import { UseAuth } from "../shared/middleware/UseAuth"
import { Role } from "#generated"
#Service()
#Resolver(() => Issue)
export default class IssueResolver {
#Inject(() => IssueService)
issueService: IssueService
#Query(() => [Issue])
async allIssues() {
return await this.issueService.getAllIssues()
}
#Query(() => [Issue])
async futureRiskIssues() {
return await this.issueService.getFutureRiskIssues()
}
#Query(() => Issue)
async issue(#Arg("id") id: string) {
return await this.issueService.getIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async createIssue(#Arg("data") data: IssueInput) {
return await this.issueService.createIssue(data)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async deleteIssue(#Arg("id") id: string) {
return await this.issueService.deleteIssue(id)
}
#UseAuth([Role.ADMIN])
#Mutation(() => Issue)
async updateIssue(#Arg("id") id: string, #Arg("data") data: IssueInput) {
return await this.issueService.updateIssue(id, data)
}
}
I can also see from my graphql.tsx file, that these functions are recognised as follows:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createUser: User;
deleteIssue: Issue;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateMe: User;
};
export type MutationCreateUserArgs = {
data: UserCreateInput;
};
export type MutationDeleteIssueArgs = {
id: Scalars['String'];
};
export type MutationUpdateIssueArgs = {
data: IssueInput;
id: Scalars['String'];
};
I have run the codegen several times and can't think of anything else to try to force these mutations and queries to be recognised. Can anyone see a way to trouble shoot this?
My codegen.yml has:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo
When I look at the mutations available on the authentication objects (that are provided with the [boilerplate app][1] that I am trying to use), I can see that there are mutations and queries that are differently represented in the lib/graphql file. I just can't figure out how to force the ones I write to be included in this way:
export function useLoginMutation(baseOptions?: Apollo.MutationHookOptions<LoginMutation, LoginMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<LoginMutation, LoginMutationVariables>(LoginDocument, options);
}
Instead, I get all of these things, but none of them look like the above and I can't figure out which one to import into my front end form so that I can make an entry in the database. None of them look like the queries or mutations I defined in my resolver
export type IssueInput = {
description: Scalars['String'];
issueGroup: Scalars['String'];
title: Scalars['String'];
};
export type IssueListRelationFilter = {
every?: InputMaybe<IssueWhereInput>;
none?: InputMaybe<IssueWhereInput>;
some?: InputMaybe<IssueWhereInput>;
};
export type IssueRelationFilter = {
is?: InputMaybe<IssueWhereInput>;
isNot?: InputMaybe<IssueWhereInput>;
};
export type IssueWhereInput = {
AND?: InputMaybe<Array<IssueWhereInput>>;
NOT?: InputMaybe<Array<IssueWhereInput>>;
OR?: InputMaybe<Array<IssueWhereInput>>;
createdAt?: InputMaybe<DateTimeFilter>;
description?: InputMaybe<StringFilter>;
id?: InputMaybe<UuidFilter>;
issueGroup?: InputMaybe<IssueGroupRelationFilter>;
issueGroupId?: InputMaybe<UuidFilter>;
subscribers?: InputMaybe<UserIssueListRelationFilter>;
title?: InputMaybe<StringFilter>;
updatedAt?: InputMaybe<DateTimeFilter>;
};
export type IssueWhereUniqueInput = {
id?: InputMaybe<Scalars['String']>;
};
I do have this record in my graphql.tsx file:
export type Mutation = {
__typename?: 'Mutation';
createIssue: Issue;
createIssueGroup: IssueGroup;
createUser: User;
deleteIssue: Issue;
deleteIssueGroup: IssueGroup;
destroyAccount: Scalars['Boolean'];
forgotPassword: Scalars['Boolean'];
getBulkSignedS3UrlForPut?: Maybe<Array<SignedResponse>>;
getSignedS3UrlForPut?: Maybe<SignedResponse>;
login: AuthResponse;
register: AuthResponse;
resetPassword: Scalars['Boolean'];
updateIssue: Issue;
updateIssueGroup: IssueGroup;
updateMe: User;
};
but I can't say: createIssueMutation as an import in my issue.tsx where I'm trying to make a form to use to post to the database.
[1]: https://github.com/NoQuarterTeam/boilerplate
In the issue form, I get an error that says:
"resource": "/.../src/pages/issue.tsx", "owner": "typescript",
"code": "2305", "severity": 8, "message": "Module '"lib/graphql"'
has no exported member 'useCreateIssueMutation'.", "source": "ts",
"startLineNumber": 7, "startColumn": 27, "endLineNumber": 7,
"endColumn": 54 }]
and the same thing for the query
check your codegen.yml
overwrite: true
schema: "http://localhost:4000/graphql"
documents: "src/graphql/**/*.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
./graphql.schema.json:
plugins:
- "introspection"
or try something like #Resolver(Issue)
It seems like you are not generating the hooks that you are trying to import.
You can update your codegen.yml file to add the generated hooks:
schema: http://localhost:5555/graphql
documents:
- "src/components/**/*.{ts,tsx}"
- "src/lib/**/*.{ts,tsx}"
- "src/pages/**/*.{ts,tsx}"
overwrite: true
generates:
src/lib/graphql.tsx:
config:
withMutationFn: false
addDocBlocks: false
scalars:
DateTime: string
withHooks: true # <--------------------- this line
plugins:
- add:
content: "/* eslint-disable */"
- typescript
- typescript-operations
- typescript-react-apollo

SAM Template - define HttpApi with Lambda Authorizer and Simple Response

Description of the problem
I have created a Lambda function with API Gateway in SAM, then deployed it and it was working as expected. In API Gateway I used HttpApi not REST API.
Then, I wanted to add a Lambda authorizer with Simple Response. So, I followed the SAM and API Gateway docs and I came up with the code below.
When I call the route items-list it now returns 401 Unauthorized, which is expected.
However, when I add the header myappauth with the value "test-token-abc", I get a 500 Internal Server Error.
I checked this page but it seems all of the steps listed there are OK https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-http-lambda-integrations/
I enabled logging for the API Gateway, following these instructions: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging.html
But all I get is something like this (redacted my IP and request ID):
[MY-IP] - - [07/Jul/2021:08:24:06 +0000] "GET GET /items-list/{userNumber} HTTP/1.1" 500 35 [REQUEST-ID]
(Perhaps I can configure the logger in such a way that it prints a more meaningful error message? EDIT: I've tried adding $context.authorizer.error to the logs, but it doesn't print any specific error message, just prints a dash: -)
I also checked the logs for the Lambda functions, there is nothing there (all logs where from the time before I added the authorizer).
So, what am I doing wrong?
What I tried:
This is my Lambda Authorizer function which I have deployed using sam deploy, when I test it in isolation using an event with the myappauth header, it works:
exports.authorizer = async (event) => {
let response = {
"isAuthorized": false,
};
if (event.headers.myappauth === "test-token-abc") {
response = {
"isAuthorized": true,
};
}
return response;
};
and this is the SAM template.yml which I deployed using sam deploy:
AWSTemplateFormatVersion: 2010-09-09
Description: >-
myapp-v1
Transform:
- AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs14.x
MemorySize: 128
Timeout: 100
Environment:
Variables:
MYAPP_TOKEN: "test-token-abc"
Resources:
MyAppAPi:
Type: AWS::Serverless::HttpApi
Properties:
FailOnWarnings: true
Auth:
Authorizers:
MyAppLambdaAuthorizer:
AuthorizerPayloadFormatVersion: "2.0"
EnableSimpleResponses: true
FunctionArn: !GetAtt authorizerFunction.Arn
FunctionInvokeRole: !GetAtt authorizerFunctionRole.Arn
Identity:
Headers:
- myappauth
DefaultAuthorizer: MyAppLambdaAuthorizer
itemsListFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/v1-handlers.itemsList
Description: A Lambda function that returns a list of items.
Policies:
- AWSLambdaBasicExecutionRole
Events:
Api:
Type: HttpApi
Properties:
Path: /items-list/{userNumber}
Method: get
ApiId: MyAppAPi
authorizerFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/v1-handlers.authorizer
Description: A Lambda function that authorizes requests.
Policies:
- AWSLambdaBasicExecutionRole
Edit:
User #petey suggested that I tried returning an IAM policy in my authorizer function, so I changed EnableSimpleResponses to false in the template.yml, then I changed my function as below, but got the same result:
exports.authorizer = async (event) => {
let response = {
"principalId": "my-user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": event.routeArn
}]
}
};
if (event.headers.myappauth == "test-token-abc") {
response = {
"principalId": "my-user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": event.routeArn
}]
}
};
}
return response;
};
I am going to answer my own question because I have resolved the issue, and I hope this will help people who are going to use the new "HTTP API" format in API Gateway, since there is not a lot of tutorials out there yet; most examples you will find online are for the older API Gateway standard, which Amazon calls "REST API". (If you want to know the difference between the two, see here).
The main problem lies in the example that is presented in the official documentation. They have:
MyLambdaRequestAuthorizer:
FunctionArn: !GetAtt MyAuthFunction.Arn
FunctionInvokeRole: !GetAtt MyAuthFunctionRole.Arn
The problem with this, is that this template will create a new Role called MyAuthFunctionRole but that role will not have all the necessary policies attached to it!
The crucial part that I missed in the official docs is this paragraph:
You must grant API Gateway permission to invoke the Lambda function by using either the function's resource policy or an IAM role. For this example, we update the resource policy for the function so that it grants API Gateway permission to invoke our Lambda function.
The following command grants API Gateway permission to invoke your Lambda function. If API Gateway doesn't have permission to invoke your function, clients receive a 500 Internal Server Error.
The best way to solve this, is to actually include the Role definition in the SAM template.yml, under Resources:
MyAuthFunctionRole
Type: AWS::IAM::Role
Properties:
# [... other properties...]
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
# here you will put the InvokeFunction policy, for example:
- PolicyName: MyPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: 'lambda:InvokeFunction'
Resource: !GetAtt MyAuthFunction.Arn
You can see here a description about the various Properties for a role: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
Another way to solve this, is to separately create a new policy in AWS Console, which has InvokeFunction permission, and then after deployment, attach that policy to the MyAuthFunctionRole that SAM created. Now the Authorizer will be working as expected.
Another strategy would be to create a new role beforehand, that has a policy with InvokeFunction permission, then copy and paste the arn of that role in the SAM template.yml:
MyLambdaRequestAuthorizer:
FunctionArn: !GetAtt MyAuthFunction.Arn
FunctionInvokeRole: arn:aws:iam::[...]
Your lambda authorizer is not returning what is expected to be an actual lambda authorizer (an IAM policy). This could explain that internal error 500.
To fix, replace is with something like this that returns an IAM policy (or rejects):
// A simple token-based authorizer example to demonstrate how to use an authorization token
// to allow or deny a request. In this example, the caller named 'user' is allowed to invoke
// a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke
// the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty
// string, the authorizer function returns an HTTP 401 status code. For any other token value,
// the authorizer returns an HTTP 500 status code.
// Note that token values are case-sensitive.
exports.handler = function(event, context, callback) {
var token = event.authorizationToken;
// modify switch statement here to your needs
switch (token) {
case 'allow':
callback(null, generatePolicy('user', 'Allow', event.methodArn));
break;
case 'deny':
callback(null, generatePolicy('user', 'Deny', event.methodArn));
break;
case 'unauthorized':
callback("Unauthorized"); // Return a 401 Unauthorized response
break;
default:
callback("Error: Invalid token"); // Return a 500 Invalid token response
}
};
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
// Optional output with custom properties of the String, Number or Boolean type.
authResponse.context = {
"stringKey": "stringval",
"numberKey": 123,
"booleanKey": true
};
return authResponse;
}
Lots more information here : https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html#api-gateway-lambda-authorizer-lambda-function-create
Just to complete the answer. You have to add an AssumeRolePolicyDocument under Properties.
The role will then state
MyAuthFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- apigateway.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
# see answer above

Routing in Serverless nuxt app not working

So I'm getting a routing problem whenever I use nuxt ssr with serverless. When I use either deploy to AWS lambda or use serverless-offline it generates the url prefixed with /{stage}, but nuxt can't seem to handle this and either throws 403, 404 or 500 errors because the routes to static files aren't prefixed with /{stage}.
I have tried adding {stage} to the public path on build, the results in a 404 because now the static file path needs to prefixed with another /{stage}. If I go directly to {stage}/{stage}/_nuxt/{file} it works.
build: {
publicPath: '/{stage}/_nuxt'
}
So looking around I found that I can update the router base to the below
router: {
base: '/{stage}'
}
but now the file only loads if its {stage}/{stage}/{stage}/_nuxt/{file} and removing the publicPath code above doesn't make it work either.
And this is for the static files, when it comes to the actual routes the homepage set at '/' either works but any other pages don't because the nuxt-links to them aren't prefixed with /{stage} or if I add the prefix to the base I get a Cannot GET / error when I visit /{stage}.
I have tried many different ways of doing this such as using express however I have had no luck and any tutorials that I found online are at least 2 years old and the github repos have the same problem. The closest thing I have found on stackoverflow that is somewhat similar to what I have is here but this is for a static site.
Anybody have any ideas? Below is the code for the serverless.yaml, handler.js, nuxt.js, nuxt.config.js.
Github Repo
serverless.yaml
service: nuxt-ssr-lambda
provider:
name: aws
runtime: nodejs12.x
stage: ${env:STAGE}
region: eu-west-1
lambdaHashingVersion: 20201221
environment:
NODE_ENV: ${env:STAGE}
apiGateway:
shouldStartNameWithService: true
functions:
nuxt:
handler: handler.nuxt
events:
- http: ANY /
- http: ANY /{proxy+}
plugins:
- serverless-apigw-binary
- serverless-dotenv-plugin
- serverless-offline
custom:
apigwBinary:
types:
- '*/*'
handler.js
const sls = require('serverless-http')
const binaryMimeTypes = require('./binaryMimeTypes')
const nuxt = require('./nuxt')
module.exports.nuxt = sls(nuxt, {
binary: binaryMimeTypes
})
nuxt.js
const { Nuxt } = require('nuxt')
const config = require('./nuxt.config.js')
const nuxt = new Nuxt({ ...config, dev: false })
module.exports = (req, res) =>
nuxt.ready().then(() => nuxt.server.app(req, res))
nuxt.config.js
module.exports = {
telemetry: false,
head: {
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' }
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
]
},
css: [
],
plugins: [
],
components: true,
buildModules: [
'#nuxtjs/tailwindcss',
],
modules: [
],
router: {
base: '/prod'
},
build: {
}
}
Are you passing it as
<p>Path: {{ $route.path }}</p>
<NuxtLink to="/">Back to Mountains</NuxtLink>
if Yes then it should work else try going with redirect('/{stage}/_nuxt')
for an if statement put this inside else , I think it should work.

How do you "DependsOn" a Lambda function from a CFN resource?

In Serverless, I am creating an AWS::Lambda::Permission CFN resource that allows Cognito to invoke a Custom Message Lambda Handler.
AWS::Lambda::Permission depends on the lambda. How do I ensure that the lambda is created first?
I have already tried adding a DependsOn property to the AWS::Lambda::Permission CFN resource with no luck.
Below is my CFN resource that is trying to add permissions to Cognito to invoke a lambda:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:invokeFunction
Principal: cognito-idp.amazonaws.com
FunctionName: arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-cognitoCustomMessage
SourceArn: arn:aws:cognito-idp:${self:provider.region}:#{AWS::AccountId}:userpool/${self:provider.environment.USER_POOL_ID}
Here is what my lambda looks like in my serverless.yml:
cognitoCustomMessage:
handler: src/main/lambdas/users_handler.cognitoCustomMessage
Here is what my lambda is doing on a very basic level:
cognitoCustomMessage(event, next) {
if (event.triggerSource === 'CustomMessage_ForgotPassword') {
// do stuff
}
return next(null, event);
}
The error I get from the above is:
An error occurred: CognitoCustomMessageLambdaFunction - Function not
found:
arn:aws:lambda:us-west-2:1234567890:my-service-dev-cognitoCustomMessage
(Service: AWSLambdaInternal; Status Code: 404; Error Code:
ResourceNotFoundException; Request ID:
e2a98525-5090-4d0f-a1f5-20610474f93b).
If I add a DependsOn:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: arn:aws:lambda:${self:provider.region}:#{AWS::AccountId}:function:${self:service}-${self:provider.stage}-cognitoCustomMessage
...
....
The error I get from above is:
The CloudFormation template is invalid: Template format error:
DependsOn must be a string or list of strings.
I have also tried:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: CognitoCustomMessageLambdaFunction
...
....
The error I get from above is:
An error occurred: CognitoCustomMessageLambdaFunction - Function not
found:
arn:aws:lambda:us-west-2:1234567890:my-service-dev-cognitoCustomMessage
(Service: AWSLambdaInternal; Status Code: 404; Error Code:
ResourceNotFoundException; Request ID:
b888ae82-a0d7-4d69-888e-9e63027925c1).
I expect that there should be some method to create a lambda function first prior to the CFN resource needing to use it, but this does not seem to be the case with DependsOn.
The DependsOn attribute should have the logical name of the Lambda Function in the Cloud formation template not the ARN of Lambda Function. For example, if your Lambda function's logical name in the Cloud Formation template is MyLambda then DependsOn should be like this:
UserPoolLambdaInvokePermission:
Type: AWS::Lambda::Permission
DependsOn: MyLambda
NOTE: open cloud formation template .serverless/cloudformation-template-update-stack.json and look for logical lambda function name.
Example:
{
"MonitorLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": "deploy-dev",
"S3Key": "serverless/dev/1641551717730-2022-01-07T10:35:17.730Z/zip"
},
"Handler": "src/monitors/handler.devMonitor",
"Runtime": "nodejs14.x",
"FunctionName": "dev-monitor",
"MemorySize": 1024,
"Timeout": 6,
"Environment": {
"Variables": {
"STAGE": "dev",
}
},
"Role": {
"Fn::GetAtt": [
"monitorIamRoleLambdaExecution",
"Arn"
]
}
}
}
MonitorLambdaFunction is the name you are looking for.
Your function defined in serverless.yml is converted into a cloudformation resource under the hood. The resource is called XLambdaFunction where X = The name of your function with the first letter capitalized.
So if you have:
functions:
hello:
handler: handler.hello
...other function stuff...
You can reference:
DependsOn: HelloLambdaFunction
I can only assume that if your function is already named CognitoCustomMessageLambdaFunction then you would have to reference:
DependsOn: CognitoCustomMessageLambdaFunctionLambdaFunction
I faced similar issue, adding dependsOn key worked for me.

CloudFormation Transform::Include parameters

I want to use AWS macro Transform::Include with some dynamic parameters for my file.
Resources:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
TestMacroVariable:
Default: 2
Type: Number
Location: !Sub "s3://${InstallBucketName}/test.yaml"
test.yaml:
DataAutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
LaunchConfigurationName:
Ref: DataLaunchConfiguration
MinSize: '1'
MaxSize: '100'
DesiredCapacity:
Ref: TestMacroVariable
...
After calling: aws cloudformation describe-stack-events --stack-name $stack
I get:
"ResourceStatusReason": "The value of parameter TestMacroVariable
under transform Include must resolve to a string, number, boolean or a
list of any of these.. Rollback requested by user."
When I try to do it this way:
Resources:
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
TestMacroVariable: 2
Location: !Sub "s3://${InstallBucketName}/test.yaml"
I get:
"ResourceStatusReason": "Template format error: Unresolved resource
dependencies [TestMacroVariable] in the Resources block of the
template"
Error is the same when I don't provide TestMacroVariable at all.
Tried with different types: String, Number, Boolean, List - none of them work.
As i know you cannot have anything other than Location key in the Parameters section of the AWS::Include. Check here AWS DOC
As an alternative, you can pass in the whole S3 path as a parameter and reference it in Location:
Parameters:
MyS3Path:
Type: String
Default: 's3://my-cf-templates/my-include.yaml'
...
'Fn::Transform':
Name: 'AWS::Include'
Parameters:
Location: !Ref MyS3Path
Building on what #BatteryAcid Said you can refer the parameters in your Cloudformation template directly from your file using Sub function:
In your CF template :
Parameters:
TableName:
Type: String
Description: Table Name of the Dynamo DB Users table
Default: 'Users'
In the file you are including:
"Resource": [
{
"Fn::Sub": [
"arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tableName}",
{
"tableName": {
"Ref": "TableName"
}
}
]
}
Alternatively doesn't have to be a parameter but any resource from your template :
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${QueryTelemtryFunction.Arn}/invocations

Resources