(appsync): Cross-stack resources -> Fn::GetAtt references undefined
See original GitHub issueI have 2 stacks:
- API-stack which contains the Appsync API
- SQS-stack, which contains a SQS resource (other stack are removed for simplicity)
I want to simply pass the SQS queue (defined in the SQS stack) to the API stack
The code synthesizes correctly, but it fails at deployment with error:
❌ AaaApiStack failed: Error [ValidationError]: Template error: instance of Fn::GetAtt references undefined resource StudentQueue68D52AA1
at Request.extractError (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/protocol/query.js:50:29)
at Request.callListeners (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
at Request.emit (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
at Request.emit (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:688:14)
at Request.transition (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/request.js:690:12)
at Request.callListeners (/usr/local/lib/node_modules/aws-cdk/node_modules/aws-sdk/lib/sequential_executor.js:116:18) {
code: 'ValidationError',
time: 2021-01-31T13:22:49.329Z,
requestId: '7f7feb96-4b35-46b1-bc67-e3c3f0d1aa2f',
statusCode: 400,
retryable: false,
retryDelay: 445.1091591510583
}
Template error: instance of Fn::GetAtt references undefined resource StudentQueue68D52AA1
Reproduction Steps
cdk.ts
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import { SQSStack } from "../lib/sqs/stack";
import { ApiStack } from "../lib/api/stack";
const app = new cdk.App();
const env = {
region: "us-east-1",
account: "446416672052",
};
const SQSStack = new SQSStack(
app,
"AaaSQSStack",
{
env,
}
);
const apiStack = new ApiStack(app, "AaaApiStack", {
env,
SQSSqs: SQSStack.studentQueue,
});
API-stack
import * as cdk from "@aws-cdk/core";
import * as node from "@aws-cdk/aws-lambda-nodejs";
import * as appsync from "@aws-cdk/aws-appsync";
import * as sqs from "@aws-cdk/aws-sqs";
import { ResolvableField } from "@aws-cdk/aws-appsync";
import * as fs from "fs";
import { join } from "path";
export interface ApiStackProps extends cdk.StackProps {
SQSSqs: sqs.IQueue;
}
export class ApiStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props: ApiStackProps) {
super(scope, id, props);
const account = props.env?.account || "";
const region = props.env?.region || "";
const api = new appsync.GraphqlApi(this, "AaaApi", {
name: "AaaApi",
xrayEnabled: true,
});
const mockQuery = new appsync.ResolvableField({
returnType: appsync.GraphqlType.string(),
dataSource: api.addNoneDataSource("none"),
requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
});
api.addQuery("mockQuery", mockQuery);
// Matching Calling Recording Stack
const { SQSSqs } = props;
const sqsDs = api.addHttpDataSource(
"SqsDs",
`https://sqs.${region}.amazonaws.com/`
);
const requestMappingTemplate = fs
.readFileSync(
join(
__dirname,
"/resolvers/sqs/sendSqs/request.vtl"
),
{
encoding: "utf-8",
}
)
.replace("ACCOUNT_ID", account)
.replace("QUEUE_NAME", SQSSqs.queueName);
const sendSqs = new appsync.ResolvableField({
returnType: appsync.GraphqlType.string(),
dataSource: sqsDs,
requestMappingTemplate: appsync.MappingTemplate.fromString(
requestMappingTemplate
),
responseMappingTemplate: appsync.MappingTemplate.fromFile(
join(
__dirname,
"/resolvers/sqs/sendSqs/response.vtl"
)
),
});
api.addMutation("sendSqsStudentRequest", sendSqs);
// const role = new iam.Role(stack, 'Role', {
// assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
// });
// api.grant(role, appsync.IamResource.custom('types/Mutation/fields/updateExample'), 'appsync:GraphQL')
}
}
SQS stack
import * as cdk from "@aws-cdk/core";
import * as node from "@aws-cdk/aws-lambda-nodejs";
import * as appsync from "@aws-cdk/aws-appsync";
import * as sqs from "@aws-cdk/aws-sqs";
import { ResolvableField } from "@aws-cdk/aws-appsync";
import * as fs from "fs";
import { join } from "path";
export class SQSStack extends cdk.Stack {
readonly studentQueue: sqs.IQueue;
constructor(scope: cdk.Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
this.studentQueue = new sqs.Queue(this, "StudentQueue");
}
}
What did you expect to happen?
CDK should have handled the routing and make sure that the appropriate resources (e.g. StudentQueue68D52AA1) was exported correctly
What actually happened?
The appropriate resource was not exported from SQS-stack
Environment
- CDK CLI Version : 1.87.1
- Framework Version:
- Node.js Version: v14.15.4
- OS : 10.15.7 (19H114)
- Language (Version): TypeScript 3.9.7
Other
This issue might be similar https://github.com/aws/aws-cdk/issues/3619
Synth SQS stack
Resources:
StudentQueue68D52AA1:
Type: AWS::SQS::Queue
Metadata:
aws:cdk:path: AaaSQSStack/StudentQueue/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Modules: aws-cdk=1.87.1,@aws-cdk/aws-appsync=1.87.1,@aws-cdk/aws-cloudwatch=1.87.1,@aws-cdk/aws-iam=1.87.1,@aws-cdk/aws-kms=1.87.1,@aws-cdk/aws-sqs=1.87.1,@aws-cdk/cloud-assembly-schema=1.87.1,@aws-cdk/core=1.87.1,@aws-cdk/cx-api=1.87.1,@aws-cdk/region-info=1.87.1,jsii-runtime=node.js/v14.15.4
Metadata:
aws:cdk:path: AaaSQSStack/CDKMetadata/Default
Synth API stack
Resources:
AaaApi72C55F6E:
Type: AWS::AppSync::GraphQLApi
Properties:
AuthenticationType: API_KEY
Name: AaaApi
XrayEnabled: true
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/Resource
AaaApiSchema7879F737:
Type: AWS::AppSync::GraphQLSchema
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
Definition: >
schema {
query: Query
mutation: Mutation
}
type Query {
mockQuery: String
}
type Mutation {
sendSqsStudentRequest: String
}
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/Schema
AaaApiDefaultApiKeyDF40679C:
Type: AWS::AppSync::ApiKey
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
DependsOn:
- AaaApiSchema7879F737
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/DefaultApiKey
AaaApinone4B86ED1B:
Type: AWS::AppSync::DataSource
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
Name: none
Type: NONE
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/none/Resource
AaaApiSqsDsServiceRole12FDDD19:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: appsync.amazonaws.com
Version: "2012-10-17"
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/SqsDs/ServiceRole/Resource
AaaApiSqsDs580AB52D:
Type: AWS::AppSync::DataSource
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
Name: SqsDs
Type: HTTP
HttpConfig:
Endpoint: https://sqs.us-east-1.amazonaws.com/
ServiceRoleArn:
Fn::GetAtt:
- AaaApiSqsDsServiceRole12FDDD19
- Arn
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/SqsDs/Resource
AaaApiQuerymockQueryResolver3471C8E8:
Type: AWS::AppSync::Resolver
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
FieldName: mockQuery
TypeName: Query
DataSourceName: none
Kind: UNIT
RequestMappingTemplate: '{"version" : "2017-02-28", "operation" : "Scan"}'
ResponseMappingTemplate: $util.toJson($ctx.result.items)
DependsOn:
- AaaApinone4B86ED1B
- AaaApiSchema7879F737
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/QuerymockQueryResolver/Resource
AaaApiMutationsendSqsStudentRequestResolver9F64A029:
Type: AWS::AppSync::Resolver
Properties:
ApiId:
Fn::GetAtt:
- AaaApi72C55F6E
- ApiId
FieldName: sendSqsStudentRequest
TypeName: Mutation
DataSourceName: SqsDs
Kind: UNIT
RequestMappingTemplate:
Fn::Join:
- ""
- - >-
{
"version": "2018-05-29",
"method": "GET",
"resourcePath": "/446416672052/
- Fn::GetAtt:
- StudentQueue68D52AA1
- QueueName
- >-
",
"params": {
"query": {
"Action": "SendMessage",
"Version": "2012-11-05",
"MessageBody": "$util.urlEncode($util.toJson($ctx.args.input))"
}
}
}
ResponseMappingTemplate: >-
#set( $result = $utils.xml.toMap($ctx.result.body) )
{
"id": "$result.SendMessageResponse.SendMessageResult.MessageId",
"email": "$ctx.args.input.email",
"message": "$ctx.args.input.message",
}
DependsOn:
- AaaApiSchema7879F737
- AaaApiSqsDs580AB52D
Metadata:
aws:cdk:path: AaaApiStack/AaaApi/MutationsendSqsStudentRequestResolver/Resource
CDKMetadata:
Type: AWS::CDK::Metadata
Properties:
Modules: aws-cdk=1.87.1,@aws-cdk/aws-appsync=1.87.1,@aws-cdk/aws-cloudwatch=1.87.1,@aws-cdk/aws-iam=1.87.1,@aws-cdk/aws-kms=1.87.1,@aws-cdk/aws-sqs=1.87.1,@aws-cdk/cloud-assembly-schema=1.87.1,@aws-cdk/core=1.87.1,@aws-cdk/cx-api=1.87.1,@aws-cdk/region-info=1.87.1,jsii-runtime=node.js/v14.15.4
Metadata:
aws:cdk:path: AaaApiStack/CDKMetadata/Default
This is 🐛 Bug Report
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:10 (6 by maintainers)
Top Results From Across the Web
Template error: instance of Fn::GetAtt references undefined ...
I think you have to add the logical name of resource. Replace EventHandlerLambdaFunction to actual resource name eventHandler .
Read more >Fn::GetAtt - AWS CloudFormation
The name of the resource-specific attribute whose value you want. See the resource's reference page for details about the attributes available for that...
Read more >awslabs/aws-cdk - Gitter
@mjburling Its bundling up the resources with the lambda handler. ... /aws-cdk-how-do-i-reference-cross-stack-resources-in-same-app/61580038#61580038.
Read more >Fn::GetAtt - Amazon CloudFormation - 亚马逊云科技
See the resource's reference page for details about the attributes available for that resource type. Return value. The attribute value. Examples. Return a ......
Read more >Template error: instance of Fn::GetAtt references undefined ...
... the following error: Error: The CloudFormation template is invalid: Template error: instance of Fn::GetAtt references undefined resource…
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
found the problem. In API-stack import the property Queue from SQS-stack and I use it inside the
.replace()
function (not inside a cdk construct). Cdk, which should be in charge or creating the “wiring” between stacks, ignores the properties that are not used inside stacks, including the IQueue. As a workaround, I just instantiated the following resource (which I am not using) to setup the wiring.Since
CfnOutput
is a construct, the queueName is now exported from SQS stack and imported in API stack and can be use.Why is this workaround necessary? Shouldn’t cdk take care of this situations without the need of a component?
@ruggero-balteri you’re right it should. This is likely a bug in appsync.