How get apiaivyGraphQLAPIIdOutput in auto generated PostConfirmation Lambda?
i tried function update, and allow access to appSync, but this dosent work, because a dependsOn Failure.
Any infos?
@davidbiller What exactly is the error you’re seeing?
It was a circular dependency error.
Should I do it again, and copy the whole error message?
I'm having the same issue when I do amplify update function
and add api
category access to the lambda post-confirmation trigger:
Ericks-MBP:app Erick$ amplify update function
Using service: Lambda, provided by: awscloudformation
? Please select the Lambda Function you would want to update icontainerPostConfirmation
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project?
Yes
? Select the category api
Api category has a resource called icontainer
? Select the operations you want to permit for icontainer create, read, update, delete
You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var apiIcontainerGraphQLAPIIdOutput = process.env.API_ICONTAINER_GRAPHQLAPIIDOUTPUT
var apiIcontainerGraphQLAPIEndpointOutput = process.env.API_ICONTAINER_GRAPHQLAPIENDPOINTOUTPUT
? Do you want to edit the local lambda function now? No
Then pushing:
Circular dependency between resources: [functionme, authicontainer, functiondeleteDirectory, functionicontainerPostConfirmation, apiicontainer]
edit: here is my backend-config.json https://gist.github.com/ErickTamayo/2fc64c7d886527f968f28c58727be2a7
I second that. Also looking for the way to pass API ID (for the table names) to the postConfirmation lambda.
Same here, if you give a congnito trigger lambda withamplify update function
access to the api there a Circular dependency between resources
error that appear when you push. It should be possible to make it work with a custom resource. But I begin with amplify and don't really know where and how to put it.
I'm also had this problem, but when trying to allow lambda access to auth. I have created a PostConfirmation trigger for cognito auth (through amplify update auth) and want to set the value of a custom cognito attribute for the user when they confirm their account. (using aws-sdk adminUpdateUserAttributes).
? Please select the Lambda Function you would want to update mycognitoPostConfirmation
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project? Yes
? Select the category auth
Auth category has a resource called mycognito
? Select the operations you want to permit for mycognito update
You can access the following resource attributes as environment variables from your Lambda function
var environment = process.env.ENV
var region = process.env.REGION
var authMycognitoUserPoolId = process.env.AUTH_MYCOGNITO_USERPOOLID
Error: Cannot add mycognitoPostConfirmation due to a cyclic dependency
at checkForCyclicDependencies (/usr/local/lib/node_modules/@aws-amplify/cli/src/extensions/amplify-helpers/update-amplify-meta.js:243:11)
at Object.updateamplifyMetaAfterResourceUpdate (/usr/local/lib/node_modules/@aws-amplify/cli/src/extensions/amplify-helpers/update-amplify-meta.js:119:5)
at Object.updateResource (/usr/local/lib/node_modules/@aws-amplify/cli/node_modules/amplify-category-function/provider-utils/awscloudformation/index.js:243:21)
at process._tickCallback (internal/process/next_tick.js:68:7)
Currently my workaround is to manually edit the auth cloudformation template and add the permissions, however it would be better if we could do this through the cli.
Is there an update?
I also did a function update and add permissions to API to access the appsyncid.
I need this id in the lamba cognito trigger post confirmation to send a userid to the dynamodb table: tablename-appsyncID-environment
But I received an Circular dependency error
Having same issue
+1
Any updates?
Same issue here at the moment. I need the API Id in my CF template. Anyone with a workaround for the time being?
I need the same as @rpostulart, <tablename>-<AppSyncID>-<env>
, both in the roles and as an environment variable to target the DB Table.
I am facing the same issue when updating API and adding a new endpoint
I'm having the same issue when adding API access to my postConfirmation function. I tried to update the function again to remove API access, and I still get the circular dependency error. I'm not sure how to undo the issue.
I'm having the same issue when adding API access to my postConfirmation function. I tried to update the function again to remove API access, and I still get the circular dependency error. I'm not sure how to undo the issue.
Amplify env checkout yourEnvName —restore
Alternatively you can search through the cloudformation-template and delete all references to the the post confirmation trigger. I am having the same issue, where i am unable to add access to my graphql api in the post confirmation trigger, the same circular reference occurs.
@kaustavghosh06 any updates on this very common problem? Is there a recommended best practice from the Amplify team to work around this?
Right now, I am updating the IAM role permissions manually to allow my lambda function permission to call my GraphQL api. Manually updating IAM roles is not ideal at all. I don't mind writing CloudFormation to do this but it is not clear how to tie in a custom child CloudFormation template into the Amplify backend CloudFormation template/stack hierarchy.
Having documentation for Amplify that shows how to update IAM roles with custom policy statements would be very beneficial for everyone using Amplify and could solve this problem scenario as well as others.
Thoughts?
Thanks,
Erik
+1
+1
+1
+1 didn't find a good solution yet. would love to get some hints on how to do this correctly. I guess one must create a custom resource and bind both api and postConfirmation function to those as dependencies? not sure tho.
I do this:
{
"Effect": "Allow",
"Action": [
"lambda:*"
],
"Resource": [
"*"
]
}
(if you try to actually specify the lambda by referencing it, you will get get circular reference problem, but you could possibly just use the name in the string instead of *, and as action just specify execute lambda, if you want to use the principle of giving minimal required permissions)
const env = process.env.ENV
const functionName = 'theNameYouGaveTheOtherFunction-' + env
const payload = {
sub: sub,
**other data***
}
lambda.invoke({
FunctionName: functionName,
Payload: JSON.stringify(payload)
}).promise().then(response0 => {
console.log("Lambda with AppSync access called")
const response = JSON.parse(response0.Payload)
**etc***
@tgjorgoski well, its kinda workaround, but at least you figured out how to do this!
Could you please explain point 2 a bit more in detail? how do you add permissions? what do you mean by "not referencing it but use simple strings"?
Hi @martinjuhasz , I updated my previous comment with more info.
Thanks @tgjorgoski for the guidance you laid out. I did something similar, where I created a separate lambda function, which is called by the trigger (Pre Token Trigger in my case) lambda function. This does seem to slow down the login process quite a bit, since I have one lambda waiting on another, which probably isn't good practice. Did you experience the same thing and did you find any way around it, or perhaps alternatives to invoking a secondary lambda function?
Sorry @ngnathan , I missed the question - for the performance I noticed that if I remove the default "index.js" which amplify cli is generating, and simply copy the main logic from the trigger inside it, the performance gets much better. I think the actual lambda calling each other is not that bad.
@tgjorgoski Thank you so much for the suggestion. Great workaround. Works well!
Thanks @tgjorgoski - that's a great tip!
I did end up completely switching my Pre Token and Post Confirmation logic up. Instead of using a lambda to invoke another lambda, I used a lambda to add the lambda as a Pre Token Trigger / Post Confirmation Trigger. This seems to work well for my purposes. I've documented that in my comment on this thread #3430 (comment)
I want to save the user details in a DynamoDB when they sign up, not when they verify.
Without adding permission to the api, the necessary env variables are not added to the pre signup lambda function, and if I add permissions to the api then I get the same circular dependency error.
Hey all,
Just following back up with the solution I used to solve this circular dependency problem. My issue was with using the Cognito Pre-Token generation lambda trigger which needed to know information about my Appsync API (API ID or endpoint). This circular dependency would occur though for any Cognito lambda trigger that needs to know the API ID or endpoint.
Dependency looks like this:
My solution was to break the circle by removing the 3rd bullet above, Cognito trigger lambda function depending on API. What I did was:
This way everything is still completely automated through the Amplify CloudFormation nested stack.
I hope this makes sense and can help others out there!
I hope everyone is staying safe and health!
Cheers,
Erik
Hey @paulsson , any chance you could share your backend-config.json? Many thanks.
@sceptyk sorry I didn't get a reply out earlier. Below is a simplified version of the backend-config.json file. Hopefully that gets you going. It will break the circular dependency that is caused by adding Cognito lambda triggers. Everything is completely automated and there is no weird workarounds with a lambda invoking another lambda.
{
"api": {
"MyAppApi": {
"service": "AppSync",
"providerPlugin": "awscloudformation",
"output": {
"authConfig": {
"additionalAuthenticationProviders": [
{
"authenticationType": "AWS_IAM"
}
],
"defaultAuthentication": {
"authenticationType": "AMAZON_COGNITO_USER_POOLS",
"userPoolConfig": {
"userPoolId": "authUserAuth"
}
}
}
}
}
},
"auth": {
"UserAuth": {
"service": "Cognito",
"providerPlugin": "awscloudformation",
"dependsOn": [
{
"category": "function",
"resourceName": "UserAuthPreTokenGeneration",
"triggerProvider": "Cognito",
"attributes": [
"Arn",
"Name"
]
}
]
}
},
"function": {
"UserAuthPreTokenGeneration": {
"service": "Lambda",
"providerPlugin": "awscloudformation",
"build": true
}
},
"iam": {
"UserAuthPreTokenGeneration": {
"service": "IAM",
"providerPlugin": "awscloudformation",
"dependsOn": [
{
"category": "api",
"resourceName": "MyAppApi",
"attributes": [
"GraphQLAPIIdOutput",
"GraphQLAPIEndpointOutput"
]
},
{
"category": "function",
"resourceName": "UserAuthPreTokenGeneration",
"attributes": [
"LambdaExecutionRole"
]
}
]
}
}
}
NOTE: The "iam" category section in the above backend-config.json is something I arbitrarily named and added since amplify cli doesn't have such a category.
You must run 'amplify env checkout
You can create other AWS resources in a similar fashion that aren't yet supported in amplify CLI. For example, I create KMS keys for encrypting / decrypting secrets stored in SSM Parameter Store by adding a "kms" category in backend-config.json.
@tgjorgoski @chrisco255 @ngnathan @amuresia @martinjuhasz this may be of interest to you as well. You likely won't really want to allow your workaround lambda function to invoke any other function in your account by granting it the below IAM permissions as that goes against security best practices of "least privilege".
{
"Effect": "Allow",
"Action": [
"lambda:*"
],
"Resource": [
"*"
]
}
@davidbiller tagging you since you originally opened this issue.
I hope this can help others out since this seems like a very common issue when needing to use Cognito triggers.
I can provide more details if needed.
Cheers!
Erik
Hi @paulsson, I really like your approach to this problem. Thanks for sharing the backend-config.json, this is helpful. Do you think you can share some example source code for your entire custom "iam" service in the backend directory, as well as the changes you make to the cloudformation-template for the lambda function? I understand what you are describing, but it would be great to have example code for the entire modification, since it is not trivial and everyone's problem here is almost identical..
So much appreciated!
Ky
FYI for anyone still looking for a decent work around on this..
My work around for time being is to manage the trigger lambda functions externally via Serverless Framework. It is quite simple since there is native support for cognito triggers in serverless.yml
like this:
functions:
PreTokenGeneration:
handler: PreTokenGeneration.handler
events:
- cognitoUserPool:
pool: <nameOfUserPool>
trigger: PreTokenGeneration
existing: true
It isn't perfect but it is absolutely simple and practical. After trying a few options from this thread, I've come to the conclusion that they are too complicated or indirect, so managing the functions cleanly at the cost of having them in a different framework feels reasonable (to be fair, I do use Serverless for other parts of my app so I have it integrated already)
@paulsson that solution looks quite elegant. Just set that up and works like a charm. In addition to your note above for anyone reading, there needs to be a cloud formation template which would apply a policy AmplifyResourcesPolicy
(if you want to copy it from another lambda function) to the role.
@paulsson that solution looks quite elegant. Just set that up and works like a charm. In addition to your note above for anyone reading, there needs to be a cloud formation template which would apply a policy
AmplifyResourcesPolicy
(if you want to copy it from another lambda function) to the role.
What does the policy need to contain exactly? Do you have an example? I've made the changes that @paulsson suggested with a custom "iam/functionName" folder containing a parmaters.json/template.json file but I'm still getting the circular dependency issue...
@ammarkarachi @dabit3 @kaustavghosh06 - do you know if this will be resolved any time soon? This issue has been open almost a year now :(
Best regards,
Kyle
So after much hacking I've got this working thanks to @paulsson ! Thanks for taking the time to write the steps on how you solved this!
@kylekirkby Sorry AWS beginner here, can you share your parameters.json and template.json configs? I'm having some issues creating the LambdaExecutionRole
cc @paulsson
Hi @quorth0n,
I feel your pain. It took me a few hours of debugging and amplify push
ing to get working. I've added my GraphQL API Endpoint URL to SSM with the current environment to ensure this works envs. As stated by @paulsson above you need to modify your backend-config.json
file and add a custom resource. Once you've added your custom resource make sure to run amplify env checkout develop
to ensure your changes are pulled in. My custom CloudFormation resource stack ended up looking something like this:
/amplify/backend/backend-config.json
{
...
"api": {
...
},
"analytics": {
...
},
"lambdaTriggerPermissions": {
"postConfirmationPermissions": {
"service": "Cognito-Lambda-Trigger-Permissions",
"providerPlugin": "awscloudformation",
"dependsOn": [
{
"category": "api",
"resourceName": "{name-of-your-api-resource}",
"attributes": [
"GraphQLAPIIdOutput",
"GraphQLAPIEndpointOutput"
]
},
{
"category": "function",
"resourceName": "{name-of-your-post-confirmation-lambda-function}",
"attributes": [
"LambdaExecutionRole"
]
}
]
}
}
}
/amplify/backend/lambdaTriggerPermissions/postConfirmationPermissions/template.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "",
"Parameters": {
"env": {
"Type": "String"
},
"api{name-of-your-api}Output": {
"Type": "String",
"Default": "api{name-of-your-api}Output"
},
"api{name-of-your-api}GraphQLAPIEndpointOutput": {
"Type": "String",
"Default": "api{name-of-your-api}GraphQLAPIEndpointOutput"
},
"function{name-of-your-api}PostConfirmationLambdaExecutionRole": {
"Type": "String",
"Default": "function{name-of-your-api}PostConfirmationLambdaExecutionRole"
}
},
"Conditions": {},
"Resources": {
"GraphQLEndpointParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Name": {
"Fn::Join": [
"",
[
"GraphQLEndpoint-",
{
"Ref": "env"
}
]
]
},
"Type": "String",
"Value": {
"Ref": "api{name-of-your-api}GraphQLAPIEndpointOutput"
},
"Description": "GraphQL API Endpoint for current stage"
}
},
"PostConfirmationCognitoResourcesPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "post-confirmation-api-execution-policy",
"Roles": [
{
"Ref": "function{name-of-your-api}PostConfirmationLambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"appsync:Create*",
"appsync:StartSchemaCreation",
"appsync:GraphQL",
"appsync:Get*",
"appsync:List*",
"appsync:Update*",
"appsync:Delete*"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:appsync:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":apis/",
{
"Ref": "api{name-of-your-api}GraphQLAPIIdOutput"
},
"/*"
]
]
}
]
}
]
}
}
}
},
"Outputs": {}
}
/amplify/backend/lambdaTriggerPermissions/postConfirmationPermissions/parameters.json
{
"api{name-of-your-api}GraphQLAPIEndpointOutput": {
"Fn::GetAtt": [
"{name-of-your-api}",
"Outputs.GraphQLAPIEndpointOutput"
]
},
"api{name-of-your-api}GraphQLAPIIdOutput": {
"Fn::GetAtt": [
"{name-of-your-api}",
"Outputs.GraphQLAPIIdOutput"
]
},
"function{name-of-your-api}PostConfirmationLambdaExecutionRole": {
"Fn::GetAtt": [
"{name-of-your-api}PostConfirmation",
"Outputs.LambdaExecutionRole"
]
}
}
Depending on what you're looking to do.. I added an additional module to the parmaeters.json file for the post confirmation function resource.
/amplify/backend/function/name-of-yourPostConfirmation/parameters.json
{
"modules": "add-to-group,create-project"
}
Then modified my create-project.js file to something like this:
/* eslint-disable-line */
const AWS = require("aws-sdk");
AWS.config.update({ region: process.env.REGION });
const addProject = require("./graphql.js").addProject;
const call_to_graphql = require("./helpers/graphql").call_to_graphql;
const ssm = new AWS.SSM();
async function getEndpoint() {
var params = {
Name: "GraphQLEndpoint-" + process.env.ENV,
WithDecryption: true,
};
var request = await ssm.getParameter(params).promise();
return request.Parameter.Value;
}
// Add a Project to DynamoDB
async function createProject(event) {
// Get GraphQL URL from SSM
var graphqlURL = await getEndpoint();
// Craft the params input array for entry to DynamoDB
const item = {
name: "My Project",
owner: event.userName,
};
call_to_graphql(item, addProject, "AddProject", graphqlURL);
return true;
}
exports.handler = async (event, context, callback) => {
await createProject(event);
};
Something that caught me by surprise (but makes sense) is that cognito lambda triggers need to be short-lived <5 seconds execution; if not then the function will be triggered multiple times. So I needed to execute a lambda function separately through a custom mutation setup in my graphql schema to do the computation of post sign up business logic.
Hope this helps yourself and others.
Cheers,
Kyle
I'm having the same problem... I have updated the auth like this:
andres@DESKTOP-CPTOQVN:~/TeVi$ amplify update auth
Please note that certain attributes may not be overwritten if you choose to use defaults settings.
You have configured resources that might depend on this Cognito resource. Updating this Cognito resource could have unintended side effects.
Using service: Cognito, provided by: awscloudformation
What do you want to do? Walkthrough all the auth configurations
Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM cont
rols (Enables per-user Storage features for images or other content, Analytics, and more)
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) Yes
Do you want to enable 3rd party authentication providers in your identity pool? No
Do you want to add User Pool Groups? No
Do you want to add an admin queries API? No
Multifactor authentication (MFA) user login options: OFF
Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
Please specify an email verification subject: Your verification code
Please specify an email verification message: Your verification code is {####}
Do you want to override the default password policy for this User Pool? No
Specify the app's refresh token expiration period (in days): 30
Do you want to specify the user attributes this app can read and write? No
Do you want to enable any of the following capabilities?
Do you want to use an OAuth flow? No
? Do you want to configure Lambda Triggers for Cognito? Yes
? Which triggers do you want to enable for Cognito Post Confirmation
? What functionality do you want to use for Post Confirmation Create your own module
Succesfully added the Lambda function locally
? Do you want to edit your custom function now? No
Successfully updated resource tevi locally
Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
And then I did amplify push
and then I have updated this function to create and update the content of the User table made with @model through the schema.graphql file:
andres@DESKTOP-CPTOQVN:~/TeVi$ amplify update function
Using service: Lambda, provided by: awscloudformation
? Please select the Lambda Function you would want to update teviPostConfirmation
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project? Yes
? Select the category storage
? Storage has 12 resources in this project. Select the one you would like your Lambda to access User:@model(appsync)
? Select the operations you want to permit for User:@model(appsync) create, update
You can access the following resource attributes as environment variables from your Lambda function
API_TEVI_GRAPHQLAPIIDOUTPUT
API_TEVI_USERTABLE_ARN
API_TEVI_USERTABLE_NAME
ENV
REGION
? Do you want to invoke this function on a recurring schedule? No
? Do you want to edit the local lambda function now? No
Successfully updated resource
Then I did amplify push
and I got this error:
andres@DESKTOP-CPTOQVN:~/TeVi$ amplify push
✔ Successfully pulled backend environment dev from the cloud.
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | -------------------- | --------- | ----------------- |
| Function | teviPostConfirmation | Update | awscloudformation |
| Auth | tevi | No Change | awscloudformation |
| Api | tevi | No Change | awscloudformation |
| Storage | s3c1026a67 | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠼ Updating resources in the cloud. This may take a few minutes...Error updating cloudformation stack
✖ An error occurred when pushing the resources to the cloud
Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
An error occured during the push operation: Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
How can I fix it?
@paulsson I followed your approach but instead of utilising SSM to store the API key, particularly if its needed as an environment variable for the lamda, I just run a bash script after my amplify push.
This may work for others who are using bash scripts currently to facilitate amplify push due to no pre or post hooks scripts.
In the bash script, I update the lambda env variables using the commandline aws tool... I end up with something like this:
#!/bin/bash
ROOT_PATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/.."
AMPLIFY_META_FILE=$ROOT_PATH/amplify/backend/amplify-meta.json
LOCAL_ENV_FILE=$ROOT_PATH/amplify/.config/local-env-info.json
region=$(cat $AMPLIFY_META_FILE | jq -r '.providers.awscloudformation.Region')
env=$(cat $LOCAL_ENV_FILE | jq -r '.envName')
cognito_userpool_id=$(cat $AMPLIFY_META_FILE | jq -r '.auth.cognitoUsers.output.UserPoolId')
appsyncApiId=$(cat $AMPLIFY_META_FILE | jq -r '.api.api.output.GraphQLAPIIdOutput')
dyanmodb_user_table_name="User-${appsyncApiId}-${env}"
variables="\
{\
\"Variables\": {\
\"ENV\": \"$env\",\
\"REGION\": \"$region\",\
\"DYNAMODB_USER_TABLE_NAME\": \"$dyanmodb_user_table_name\",\
\"COGNITO_USERPOOL_ID\": \"$cognito_userpool_id\"\
}\
}\
"
echo "Setting CognitoUser Lambda Environment Variables:"
echo $variables
aws lambda update-function-configuration \
--function-name cognitoUser-$env \
--environment "$variables"
note: you need aws cli correctly configured and jq to read json files in bash. my bash files are located in {root project directory}/scripts
@kaustavghosh06 is it possible to modify the CFT to specify an existing lambda function instead of one of the generated ones?
After creating a cognito user we want the post confirmation function to add a user to the dynamodb table to be used as the profile for a user.
When giving access to the appropriate model, we get the Circular dependency between resources.
This is a bit of a concern as it's pretty standard practice to do.
Need to get Table name without circular dependency error.
+1 I am having the same issue. Need to access the AppSync endpoint from the PostConfirmation lambda assigned to the trigger.
A work around for us, we added the graphql ids to out cloudformation which allows us to get the table name and access it by process.env.USER_TABLE_NAME
.
Additions to the cloudformation template are Mappings, Variables and the dynamodb:putitem arn permissions policy under lambdaexecutionpolicy
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Lambda resource stack creation using Amplify CLI",
"Parameters": {
"GROUP": {
"Type": "String",
"Default": ""
},
"modules": {
"Type": "String",
"Default": "",
"Description": "Comma-delimmited list of modules to be executed by a lambda trigger. Sent to resource as an env variable."
},
"resourceName": {
"Type": "String",
"Default": ""
},
"trigger": {
"Type": "String",
"Default": "true"
},
"functionName": {
"Type": "String",
"Default": ""
},
"roleName": {
"Type": "String",
"Default": ""
},
"parentResource": {
"Type": "String",
"Default": ""
},
"parentStack": {
"Type": "String",
"Default": ""
},
"env": {
"Type": "String"
}
},
"Mappings": {
"graphQLAPIIdMap": {
"dev": {
"graphQLAPIId": "xxxxxx"
},
"prod": {
"graphQLAPIId": "xxxxxx"
},
}
},
"Conditions": {
"ShouldNotCreateEnvResources": {
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
},
"Resources": {
"LambdaFunction": {
"Type": "AWS::Lambda::Function",
"Metadata": {
"aws:asset:path": "./src",
"aws:asset:property": "Code"
},
"Properties": {
"Handler": "index.handler",
"FunctionName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"BaseProjectAuthPostConfirmation",
{
"Fn::Join": [
"",
[
"BaseProjectAuthPostConfirmation",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"Environment": {
"Variables": {
"ENV": {
"Ref": "env"
},
"MODULES": {
"Ref": "modules"
},
"REGION": {
"Ref": "AWS::Region"
},
"GROUP": {
"Ref": "GROUP"
},
"USER_TABLE_NAME": {
"Fn::Join": [
"",
[
"User",
"-",
{
"Fn::FindInMap": [
"graphQLAPIIdMap",
{
"Ref": "env"
},
"graphQLAPIId"
]
},
"-",
{
"Ref": "env"
}
]
]
}
}
},
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Runtime": "nodejs10.x",
"Timeout": "25",
"Code": {
"S3Bucket": "<removed>",
"S3Key": "<removed>"
},
"Layers": []
}
},
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"BaseProjectAuthPostConfirmation",
{
"Fn::Join": [
"",
[
"BaseProjectAuthPostConfirmation",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
}
},
"lambdaexecutionpolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "lambda-execution-policy",
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": {
"Fn::Sub": [
"arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
}
}
]
}
},
{
"Effect": "Allow",
"Action": [
"dynamodb:PutItem"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:dynamodb:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":table/",
{
"Fn::Join": [
"",
[
"User",
"-",
{
"Fn::FindInMap": [
"graphQLAPIIdMap",
{
"Ref": "env"
},
"graphQLAPIId"
]
},
"-",
{
"Ref": "env"
}
]
]
}
]
]
}
]
}
]
}
}
}
},
"Outputs": {
"Name": {
"Value": {
"Ref": "LambdaFunction"
}
},
"Arn": {
"Value": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
}
},
"LambdaExecutionRole": {
"Value": {
"Ref": "LambdaExecutionRole"
}
},
"Region": {
"Value": {
"Ref": "AWS::Region"
}
}
}
}
if anyone is OK with a more manual solution to this issue, you can do the following:
@model
with { allow: private, provider: iam }
@auth
directive {
"Action": [
"appsync:Create*",
"appsync:StartSchemaCreation",
"appsync:GraphQL",
"appsync:Get*",
"appsync:List*",
"appsync:Update*",
"appsync:Delete*"
],
"Resource": [
"arn:aws:appsync:eu-central-1:2323232:apis/replace-with-yours/*"
],
"Effect": "Allow"
}
The same issue. I appreciate the "workaround" but it seems really convoluted to implement when it should be something really common. We need to access to dynamoDB tables in the pre-jwt-token generation lambda:
<tablename>-<AppSyncID>-<env>
Hey @kylekirkby ,
your solution almost worked for me but I think that the replacements are partially wrong. The one that worked for me are:
File /amplify/backend/backend-config.json
-> as suggested by @kylekirkby
File /amplify/backend/lambdaTriggerPermissions/postConfirmationPermissions/template.json
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "",
"Parameters": {
"env": {
"Type": "String"
},
"api{YOUR-API-NAME}GraphQLAPIEndpointOutput": {
"Type": "String",
"Default": "api{YOUR-API-NAME}Output"
},
"api{YOUR-API-NAME}GraphQLAPIIdOutput": {
"Type": "String",
"Default": "api{YOUR-API-NAME}GraphQLAPIEndpointOutput"
},
"function{YOUR-FUNCTION-NAME}LambdaExecutionRole": {
"Type": "String",
"Default": "function{YOUR-FUNCTION-NAME}LambdaExecutionRole"
}
},
"Conditions": {},
"Resources": {
"GraphQLEndpointParam": {
"Type": "AWS::SSM::Parameter",
"Properties": {
"Name": {
"Fn::Join": [
"",
[
"GraphQLEndpoint-",
{
"Ref": "env"
}
]
]
},
"Type": "String",
"Value": {
"Ref": "api{YOUR-API-NAME}GraphQLAPIEndpointOutput"
},
"Description": "GraphQL API Endpoint for current stage"
}
},
"PostConfirmationCognitoResourcesPolicy": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "post-confirmation-api-execution-policy",
"Roles": [
{
"Ref": "function{YOUR-FUNCTION-NAME}LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"appsync:Create*",
"appsync:StartSchemaCreation",
"appsync:GraphQL",
"appsync:Get*",
"appsync:List*",
"appsync:Update*",
"appsync:Delete*"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:appsync:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":apis/",
{
"Ref": "api{YOUR-API-NAME}GraphQLAPIIdOutput"
},
"/*"
]
]
}
]
}
]
}
}
}
},
"Outputs": {}
}
file /amplify/backend/lambdaTriggerPermissions/postConfirmationPermissions/template.json
{
"api{YOUR-API-NAME}GraphQLAPIEndpointOutput": {
"Fn::GetAtt": [
"{YOUR-API-NAME}",
"Outputs.GraphQLAPIEndpointOutput"
]
},
"api{YOUR-API-NAME}GraphQLAPIIdOutput": {
"Fn::GetAtt": [
"{YOUR-API-NAME}",
"Outputs.GraphQLAPIIdOutput"
]
},
"function{YOUR-FUNCTION-NAME}LambdaExecutionRole": {
"Fn::GetAtt": [
"function{YOUR-FUNCTION-NAME}",
"Outputs.LambdaExecutionRole"
]
}
}
Thanks @paulsson @kylekirkby .The solution works perfectly. However is there a way to call a graphql end point using a seperate lambda (due to 5 sec constraint ) using a custom SQS deployment and triggering the lambda .
After putting some effort into this issue I also got faced with I came up with the following solution.
I had the exact same use case as @MontoyaAndres: A circular dependency issue caused by a post confirmation function that needs API access (at least to get the GraphQL ID). After diving into this problem and reading tons of solutions and explanations, especially by @paulsson and @kylekirkby I came up with an own, in my opinion even simpler solution. I used the custom resource stack (amplify/backend/api/{your-api-name}/stacks/CustomResources.json
) and exported the AppSyncApiId INSTEAD of doing an amplify function update
.
[...]
"Outputs": {
[...]
"StaticGraphQLAPIId": {
"Description": "GraphQL API ID with static export name",
"Value": {
"Ref": "AppSyncApiId"
},
"Export": {
"Name": {
"Fn::Join": [
":",
[
{
"Fn::Join": [
"",
[
"api",
{
"Ref": "AppSyncApiName"
}
]
]
},
{
"Ref": "env"
},
"StaticGraphQLApiId"
]
]
}
}
}
}
Now I can use this exported value (which contains the dynamic part of all DynamoDB tables) to access DynamoDB from my Lambda function directly importing this value like this:
// amplify/backend/function/{your-post-confirmation-function}/parameters.json
{
[...]
"AppSyncApiName": "{your-api-name}"
{
// amplify/backend/function/{your-post-confirmation-function}/{your-post-confirmation-function}-cloudformation-template.json
"Parameters": {
[...]
"AppSyncApiName": {
"Type": "String",
"Description": "The name of the AppSync API",
"Default": ""
}
},
[...]
"Resources": {
[...]
"Properties": {
[...]
"Environment": {
"Variables": {
[...]
"GRAPHQLID": {
"Fn::ImportValue": {
"Fn::Join": [
":",
[
{
"Fn::Join": [
"",
[
"api",
{
"Ref": "AppSyncApiName"
}
]
]
},
{
"Ref": "env"
},
"StaticGraphQLApiId"
]
]
}
}
You now have access to process.env.GRAPHQLID from your NodeJS Lambda function and can execute DynamoDB actions according to your personal use case. For DynamoDB access you might extend your post confirmation function resource by corresponding resource policy (also in {your-post-confirmation-function}-cloudformation-template.json).
Hope it helps so that you don't have to spend hours and hours of research. Solving this circular dependency issue in amplify-cli would be appreciated.
It amazes me, that in so many common use cases for Amplify you need to circumvent Amplify and hack it yourself. I like the idea of Amplify, but being told these workarounds to dive into generated files you should rarely see (let alone edit) feels bad. At least these kinds of workarounds should not be necessary on your first tutorial implementing basic functionality. It would be great to see this handled by Amplify.
Btw: For me this problem is actually yak shaving for another basic functionality: syncing your Cognito users with the users in your AppSync. If that first thing would be included in Amplify, I wouldn't even need the Lambda permission to access AppSync in the trigger.
@morgler Absolutely. And beside of that authentication rules are not flexible enough. Simple use cases like showing all properties to some groups but exclude some for public access isn't possible in combination with connections. That's why we decided to build a good old fashioned REST API microservice architecture using serverless framework instead of Amplify Backend.
@bart I also use serverless in production - it's more mature and flexible.
However I haven't completely given up on Amplify over the past years. I still hope this develops into a mature tool that makes micro services even more convenient than serverless. My feeling is, that while two years ago I couldn't use Amplify for anything, today I can at least work around the missing pieces using serverless. I will keep playing with Amplify, but will likely rely on serverless in production for quite some time.
I initially plan to use a Post Confirmation Lambda to call AppSync and auto-create a user profile for each user at the database upon a successful confirm sign up. After reading this thread, it sounds like such a hassle to work around it in Post Confirmation Lambda due to circular dependency.
I realize I am better off letting users to call AppSync directly and create their own profile in the client side when they first login. To me, it is easier to set up a custom createProfile resolver in vtl for user to call.
For people experiencing similar issue with post confirmation lambda, I think it is possible to re-consider your use case. And if it can be moved to client side with a custom resolver, it can be an easier alternative to consider.
Cheers
@bart your solution did not work for me as. I got "No export named api
@hisham Maybe you did something different. I didn't add the dependency because as you said it would re-introduce this circular dependency problem. And I also didn't deploy in two phases. Just did it as described.
I have added dependency to storage->tablename to post-confirmation lambda to save user data, but also have the same issue.
I confused, does dynamodb has dependency on Cognito?
Update:
(09/12/2020)
This is still not working. The bug was obscured by a vague error log that only helps compound the issue, rather than fix it. Classic Amplify. Apologies to all that may have gotten their hopes up 😅
(08/12/2020)
I ran into this issue recently. I have since updated the cli tool globally and the issue seems to be fixed. YMMV!
Steps:
yarn global add @aws-amplify/cli #4.38.0
amplify push --yes
Yet another workaround is to simply not register any trigger when configuring your auth and just set them in the AWS Console afterwards (or through the AWS CLI).
While this goes outside of the Amplify CLI and is not IaC, it's arguably the easiest and quickest option, least prone to errors, maintains dynamic env vars, and involves a quick manual step on the likely least changing part of your stack. This works as far as I can tell, at least for those using an API with Cognito Auth + Cognito triggers that use API/Dynamo access.
Relevant Steps From Scratch:
Steps for those already suffering with cyclic dependency issue:
You may ask why all the trouble for the second set of steps? I'm not able to remove a single remaining trigger, hence the manual edits. See https://github.com/aws-amplify/amplify-cli/issues/5986
Note to the CLI team: You guys have done an amazing job and I'm sure addressing these problems is quite complex. Nevertheless, if you can fix issues like this, the one I linked to above, being able to add lambda environment vars from the CLI and other common things like that, I'd be much more inclined to recommend Amplify to others. It seems like the vast majority of work for a lot of use cases is done, it's just last mile nitty gritty work. Thanks for all your efforts.
As a workaround, I set up custom SQS resource and add dependency/permission to post on it from postcomfrirmation function, finally added another trigger from SQS which writes to DynamoDB.
A little bit long way but works !
@ayeganyan can you post how you set it up?
Hi @waltermvp
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Queue resource stack creation using Amplify CLI",
"Parameters": {
"env": {
"Type": "String"
}
},
"Conditions": {
"ShouldNotCreateEnvResources": {
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
},
"Resources": {
"SQS": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"postConfirmationEvent",
{
"Fn::Join": [
"",
[
"postConfirmationEvent",
"-",
{
"Ref": "env"
}
]
]
}
]
}
}
}
},
"Outputs": {
"Name": {
"Value": {
"Ref": "SQS"
}
},
"Arn": {
"Value": {
"Fn::GetAtt": ["SQS", "Arn"]
}
},
"Region": {
"Value": {
"Ref": "AWS::Region"
}
}
}
}
"queue": {
"postConfirmationQueue": {
"service": "SQS",
"providerPlugin": "awscloudformation"
}
},
...
"authenticationPostConfirmation": {
"build": true,
"providerPlugin": "awscloudformation",
"service": "Lambda",
"dependsOn": [
{
"category": "queue",
"resourceName": "postConfirmationQueue",
"attributes": [
"Name"
]
}
]
},
"postConfirmationQueueTrigger": {
"build": true,
"providerPlugin": "awscloudformation",
"service": "Lambda"
}
{
Parameters:
...
"queuepostConfirmationQueueName": {
"Type": "String"
"Description": "Should be in <category><resource><output> format"
},
"queuepostConfirmationQueueArn": {
"Type": "String"
},
....
"Environment": {
....
"SQS_POSTCONFIRMATION_QUEUE": {
"Ref": "queuepostConfirmationQueueName"
},
}
...
...
"AmplifyResourcesPolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "amplify-lambda-execution-policy",
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:SendMessage",
"sqs:GetQueueAttributes"
],
"Resource": [
{
"Fn::Sub": "${queuepostConfirmationQueueArn}"
}
]
}
]
}
}
}
{
"modules": "custom",
"resourceName": "authenticationPostConfirmation",
"queuepostConfirmationQueueArn": {
"Fn::GetAtt": ["queuepostConfirmationQueue", "Outputs.Arn"]
},
"queuepostConfirmationQueueName": {
"Fn::GetAtt": ["queuepostConfirmationQueue", "Outputs.Name"]
}
}
"Resources": {
"LambdaFunction": {
...
},
"LambdaEventSourceMappingPostConfirmationQueue": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": [
"AmplifyResourcesPolicy",
"LambdaExecutionRole"
],
"Properties": {
"BatchSize": 10,
"MaximumBatchingWindowInSeconds": 5,
"Enabled": true,
"EventSourceArn": {
"Fn::Sub": "${queuepostConfirmationQueueArn}"
},
"FunctionName": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
}
}
},
...
"AmplifyResourcesPolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "amplify-lambda-execution-policy",
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": [
{
"Fn::Sub": "${queuepostConfirmationQueueArn}"
}
]
},
....
const params = {
MessageBody: item,
QueueUrl: process.env.SQS_POSTCONFIRMATION_QUEUE,
}
const res = await sqs
.sendMessage(params)
.promise()
Hope this will help!
@waltermvp the functionality is broken in latest version (at least 4.39.0)
4.29.8 is working fine
Most helpful comment
Is there an update?
I also did a function update and add permissions to API to access the appsyncid.
I need this id in the lamba cognito trigger post confirmation to send a userid to the dynamodb table: tablename-appsyncID-environment
But I received an Circular dependency error