Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get apiaivyGraphQLAPIIdOutput in auto generated PostConfirmation Lambda? #1874

Closed
davidbiller opened this issue Jul 20, 2019 · 65 comments
Closed
Assignees
Labels
bug Something isn't working functions Issues tied to the functions category

Comments

@davidbiller
Copy link

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?

@haverchuck
Copy link
Contributor

@davidbiller What exactly is the error you’re seeing?

@davidbiller
Copy link
Author

It was a circular dependency error.
Should I do it again, and copy the whole error message?

@ErickTamayo
Copy link

ErickTamayo commented Jul 22, 2019

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

@UnleashedMind UnleashedMind added functions Issues tied to the functions category pending-triage Issue is pending triage labels Jul 23, 2019
@voidarg
Copy link

voidarg commented Jul 23, 2019

I second that. Also looking for the way to pass API ID (for the table names) to the postConfirmation lambda.

@gchablowski
Copy link

gchablowski commented Aug 8, 2019

Same here, if you give a congnito trigger lambda with amplify 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.

@DanNeish
Copy link

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.

@rpostulart
Copy link

rpostulart commented Oct 16, 2019

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

@wellini
Copy link

wellini commented Oct 21, 2019

Having same issue

@rfpedrosa
Copy link

+1

@patspam
Copy link
Contributor

patspam commented Nov 9, 2019

Any updates?

@BabyDino
Copy link

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.

@rahulvyas1
Copy link

rahulvyas1 commented Nov 15, 2019

I am facing the same issue when updating API and adding a new endpoint

@gretchen-blackwood
Copy link

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.

@davidbiller
Copy link
Author

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

@kaustavghosh06 kaustavghosh06 added this to Kaustav bugs in Issue tracking Dec 18, 2019
@grigull
Copy link

grigull commented Dec 18, 2019

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.

@paulsson
Copy link

@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

@amuresia
Copy link

+1

2 similar comments
@dtelaroli
Copy link

+1

@mdebo
Copy link

mdebo commented Jan 21, 2020

+1

@martinjuhasz
Copy link

+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.

@dylan-westbury
Copy link

dylan-westbury commented Aug 31, 2020

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"
			}
		}
	}
}

@sakhmedbayev
Copy link

sakhmedbayev commented Sep 1, 2020

if anyone is OK with a more manual solution to this issue, you can do the following:

  1. add additional IAM auth provider to your GraphQL API
  2. decorate your @model with { allow: private, provider: iam } @auth directive
  3. add the following permission to your lambda's IAM execution role:
        {
            "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"
        }

@marianolc
Copy link

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>

@edoardo849
Copy link

edoardo849 commented Sep 4, 2020

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"
    ]
  }
}

@slatemates
Copy link

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 .

@bart
Copy link

bart commented Sep 25, 2020

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.

@morgler
Copy link

morgler commented Oct 28, 2020

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.

@bart
Copy link

bart commented Oct 28, 2020

@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.

@morgler
Copy link

morgler commented Oct 28, 2020

@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.

@xitanggg
Copy link

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

@hisham
Copy link
Contributor

hisham commented Dec 2, 2020

@bart your solution did not work for me as. I got "No export named api::StaticGraphQLApiId found". Don't you need to add a dependsOn on the lambda since it now depends on the graphql api outputs? But wouldn't this re-introduce the circular issue? I think your solution would work if I do the deploy in two phases (one to export the output, the other to add the lambda), but it didn't work for me in one step.

@bart
Copy link

bart commented Dec 3, 2020

@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.

@ayeganyan
Copy link

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?

@vitalbone
Copy link

vitalbone commented Dec 8, 2020

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 aws-amplify/amplify-cli#4.38.0
amplify push --yes

@iyz91
Copy link

iyz91 commented Dec 11, 2020

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:

  • Configure auth, be sure to not set any triggers
  • Create your intended trigger functions, and provide any api/storage access you require through the CLI
  • Configure everything else as normal (e.g. API with Cognito User Pool auth)
  • Go to Cognito in AWS Console and set the relevant trigger lambdas (or through the AWS CLI)

Steps for those already suffering with cyclic dependency issue:

  • Delete any and all references to Cognito triggers in the various files
    • backend/backend-config.json: Delete relevant references in "dependsOn" property
    • backend/auth/{AuthName}/parameters.json:
      • Delete relevant references in "dependsOn" property
      • Reset "triggers" property to a blank string
    • backend/auth/{AuthName}/{AuthName}-cloudformation-template.yml:
      • Delete trigger function Arns & Names (e.g. function{FunctionName}Arn/Name)
      • Delete trigger invoke permissions (e.g. UserPoolPostConfirmationLambdaInvokePermission)
      • Delete whole LambdaConfig under UserPool in Resources section
  • Perform Steps from Scratch if you haven't already done so

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 #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.

@ayeganyan
Copy link

ayeganyan commented Dec 12, 2020

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 !

@waltermvp
Copy link

@ayeganyan can you post how you set it up?

@ayeganyan
Copy link

Hi @waltermvp

  1. Create SQS in queue category:
    amplify/backend/queue/postConfirmationQueue-cloudformation-template.json
{
  "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"
      }
    }
  }
}
  1. Add the new resource and dependencies on it in amlify/backend/backend-config.json
  "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"
}
  1. Add permission settings and SQS inputs for postConfirmation function in amplify/backend/function/authenticationPostConfirmation/authenticationPostConfirmation-cloudformation-template.json:
{
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}"
                }
              ]
            }
          ]
        }
      }
    }
  1. Pass SQS ARN and name as input in amplify/backend/function/authenticationPostConfirmation/parameters.json:
{
  "modules": "custom",
  "resourceName": "authenticationPostConfirmation",
  "queuepostConfirmationQueueArn": {
    "Fn::GetAtt": ["queuepostConfirmationQueue", "Outputs.Arn"]
  },
  "queuepostConfirmationQueueName": {
    "Fn::GetAtt": ["queuepostConfirmationQueue", "Outputs.Name"]
  }
}
  1. Create function and add event source mapping to trigger on SQS events, pass queuepostConfirmationQueueArn and queuepostConfirmationQueueName in the same way as above: amplify/backend/function/postConfirmationQueueTrigger/postConfirmationQueueTrigger-cloudformation-template.json
  "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}"
                }
              ]
            },
....
  1. Send to SQS in the function and receive in another function!
  const params = {
    MessageBody: item,
    QueueUrl: process.env.SQS_POSTCONFIRMATION_QUEUE,
  }
  const res = await sqs
    .sendMessage(params)
    .promise()

Hope this will help!

@ayeganyan
Copy link

@waltermvp the functionality is broken in latest version (at least 4.39.0)
4.29.8 is working fine

@mansdahlstrom1
Copy link

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.

Just a Follow up on @bart Answer, i have the same use case and solved it with the help of his instructions.

To give your lambda access to DynamoDB you can add the following cloudformation snippet to amplify/backend/function/{your-post-confirmation-function}/{your-post-confirmation-function}-cloudformation-template.json

{
  "Resources": {
    { ... },
    "lambdaexecutionpolicy": {
      { ... }
      "Properties": {
        { ... }
        "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            { ... }
            {
              "Effect": "Allow",
              "Action": [
                "dynamodb:BatchGet*",
                "dynamodb:DescribeStream",
                "dynamodb:DescribeTable",
                "dynamodb:Get*",
                "dynamodb:Query",
                "dynamodb:Scan",
                "dynamodb:BatchWrite*",
                "dynamodb:CreateTable",
                "dynamodb:Delete*",
                "dynamodb:Update*",
                "dynamodb:PutItem"
              ],
              "Resource": "*"
            }
          ]
        }
      }
    }
  }
}

Please not that you can also change "Resource" to only allow access to a specific table, see more here: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_dynamodb_specific-table.html

Also you can remove / edit allowed Action to fit your use case!

@attilah
Copy link
Contributor

attilah commented Jan 12, 2021

Closing in favor of: #4568

@github-actions
Copy link

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 25, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working functions Issues tied to the functions category
Projects
Development

No branches or pull requests