How can I split the appsync into multiple stack? - amazon-web-services

I have a project to deploy a appsync API using this plugin (https://github.com/sid88in/serverless-appsync-plugin). And I am looking for a solution to split all infra. into multiple stacks (multiple serverless.yml file).
My project structure looks like:
main/serverless.yml
dataSources/serverless.yml
resolvers/serverless.yml
schema/serverless.yml
The main folder only deploys the Appsync instance and logging and authentication. It doesn't include any schema, resolvers etc.
And other folders each of which is to deploy schema, resolvers, dataSources to the Appsync deployed by the main folder. In these folders, they need to import the appsync infra in order to attach these resolvers.
That means there will be multiple cloudformation stacks created and using cross stack reference among them. I wonder how I can make this by using this plugin.

In general you can export CF variables that you might need in other stacks using Output such as:
resources:
Resources:
NotesTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: notes
# ...
Outputs:
Value:
Ref: NotesTable
Export:
Name: NotesTableName
And in another file read them like: 'Fn::ImportValue': NotesTableName
I am using examples from a wonderful source:
https://serverless-stack.com/chapters/cross-stack-references-in-serverless.html
Your example is AppSync...
You can define your data sources such as DynamoDb in a dedicated stack and in the AppSync sources only need to reference them by name such as:
- type: AMAZON_DYNAMODB
name: ItemSource
description: Item table
config:
tableName: ItemTable // Whatever name you used in your DynamoDb stack
For lambdas you would use the Output/import. In resolvers:
- type: AWS_LAMBDA
name: SomeLambdaSource
config:
functionName: someSource
lambdaFunctionArn:
Fn::ImportValue: SomeLambdaSource
In your lambda stack:
functions:
someSource:
name: SomeSource
handler: src/handlers/someSource.handler
resources:
Outputs:
SomeSource:
Value:
Fn::GetAtt:
- SomeSource
- Arn
Export:
Name: SomeSource

Related

AWS API Gateway: Api Event must have a String specified for 'Path'

I'm trying to deploy an AWS API Gateway and a Lambda function using Gitlab CICD pipeline using sam template. Since I want individual stacks for each environment, the Path property under Events of the Lambda function needs to change with each environment. Below is my code for implementing this:
Mappings:
StackEnv:
stack-dev:
envm: "dev"
stack-qa:
envm: "qa"
Resources:
MyFunction:
Type: AWS::Serverless::Function
Properties:
Events:
MyAPI:
Type: Api
Properties:
Path:
Fn::Sub:
- "/${path}"
- path: !FindInMap [StackEnv, !Ref AWS::StackName, envm]
However, while running the pipeline, an error is returned:
[InvalidResourceException('MyFunction', "Event with id [MyAPI] is invalid. Api Event must have a String specified for 'Path'.")]
Is this behaviour expected while passing in values using !FindInMap? Is there any other way of dynamically passing in the value for Path?

Expose SNSTopic TopicArn in AWS CloudFormation Template: How might I expose my TopicArn in my CloudFormation script for my SNS Topic?

I'd like to expose the TopicArn Value (referenced in the outputs section at the bottom of my code snippet) of my SNStopic via Cloudformation template in the outputs tab of my stack in a similar manner to the way it's exposed in the resources when I create an SNStopic through the service catalog. I tried to access it by referencing it in the outputs section of my yaml script using dot notation but have been unsuccessful thus far. How might I be able to do so? I'm looking to do this so others using my script in the future won't have to go searching for the TopicArn in another place in order to subscribe to it.
Another important thing to note is that the provisioned product id below, under the properties section of the resources code block generates an SNSTopic.
Resources:
LabTrainingSnsTopic:
Type: "AWS::ServiceCatalog::CloudFormationProvisionedProduct"
Properties:
ProductId: prod-4iafsjovqrsrm # Sns Topic
ProvisioningArtifactName: "v1.1" # Must be an actual version number.
ProvisionedProductName: !Ref ProvisionedProductName
...
Outputs:
AccountID:
Description: The account in which this was built.
Value: !Ref 'AWS::AccountId'
TopicArn:
Description: Arn of the topic we created
Value: !GetAtt LabTrainingHigSnsTopic.ProvisionedProductName.Resources.SNSTopic
service catalog screenshot
cloudformation screenshot

Multiple SAM Templates with Same Custom Domain, Different Path Mapping

I'm trying to create multiple services with different repositories; actors and movies. Each of these repositories have their own template.yaml
Unfortunately if I use the same domain but different base path in both of these template.yaml, I have errors and 2nd service is not deployed;
in actors repository; template.yaml
Mapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
BasePath: actors
DomainName: api.example.com
RestApiId: !Ref Api
Stage: !Ref Env
Api:
Type: AWS::Serverless::Api
Properties:
# ... other props
in movies repository; template.yaml
Mapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
BasePath: movies
DomainName: api.example.com
RestApiId: !Ref Api
Stage: !Ref Env
Api:
Type: AWS::Serverless::Api
Properties:
# ... other props
It looks like this can't be done. Even if I move the domain configuration within Api resource definition it says the domain is being used in another stack as the stack names are also different.
Is there any way around this?
You cannot create two resources with identical domain names. This would allow for very bad things.
Instead,
Option one 1 : Single AWS::Serverless::Api
Create one AWS::Serverless::Api resource with a single domain name and multiple Events
Option 2 : Multiple AWS::Serverless::Api
Create multiple AWS::Serverless::Api resources with unique domain names (use the default one at creation)
create a single AWS::CloudFront::Distribution resource with multiple Origins that are your AWS::Serverless::Api's and the domain name you would like it to use. This way it will "front" that domain to your many origins (api-gateways)
Notes:
You may run into some syntax issues should you decide to continue to use multiple template files, I'd recommend using a nested approach
#Revenant this seems to be possible now. Your way doing it sound very legic right now.
Having a separate API per service
Having a separate basepath per service
api.example.com/actors
api.example.com/moviews
Just make sure your service supports to be called via the root path /

AWS Serverless, CloudFormation : Error, Trying to populate non string value into a string for variable

I am using serverless framework for deploying my application on AWS Cloud.
https://serverless.com/
I want to use the value of AWS Account ID in my serverless.yml file and I want to export the acccount ID as the environment variable, so that it can be accessed from Lambda functions.
Based on the value of this lambda function, I want to create some resources ( like IAM roles, etc.), which refer to this accountId variable.
But when I try to deploy the stack, I get the below error,
Trying to populate non string value into a string for variable
${self:custom.accountId}. Please make sure the value of the property
is a string.
My Serverless.yml file is as below
custom:
accountId : !Ref "AWS::AccountId"
provider:
name: aws
runtime: go1.x
stage: dev
region: us-east-1
environment:
ACCOUNT_ID : ${self:custom.accountId}
myRoleArn: arn:aws:iam::${self:custom.accountId}:role/xxxxxxxx
Is there any way to refer to the value of the Account Id in the serverless.yml file?
You can't reference AWS::AccountId in your serverless.yml, because it doesn't quite translate when creating the CloudFormation template.
The workaround is to use the serverless plugin Pseudo Parameters.
You can install the plugin using npm.
npm install serverless-pseudo-parameters
You will also need to add the plugin to the serverless.yml file.
plugins:
- serverless-pseudo-parameters
Then you can reference your AccountId with #{AWS::AccountId}
functions:
helloWorld:
handler: index.handler
events:
- http:
path: /
method: get
environment:
ACCOUNT_ID : #{AWS::AccountId}
Note that the reference begins with a hash instead of a dollar sign.

Can I insert text from another file into my cloudformation template?

I have this for example in my template:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: MyApi
Description: My AWS API Gateway config
Body:
# INSERT swagger.yml content here
Is there some cloudformation function I can use to read swagger.yml in or attach it under "Body:"? So I can keep it in another file and my template doesn't become huge.
There's a Fn::Transform function that allows you to call different Cloudformation macros to process your templates. One of those macros is AWS::Include
Heres an example:
Resources:
APIGateway:
Fn::Transform:
Name: AWS::Include
Parameters:
Location:
Fn::Sub: s3://partials-bucket/${PartialsEnv}/resources/api-gateway.yaml
Here api-gateway.yaml will have the full definition of your resource.
You can use this function in the same way as other intrinsic functions. The only caveat is AWS::Include will only work with files hosted in S3 so you'll need to upload your partials separatedly.
You can try the BodyS3Location .
The Amazon Simple Storage Service (Amazon S3) location that points to
an OpenAPI file, which defines a set of RESTful APIs in JSON or YAML
format.
For Example
"BodyS3Location": {
"Bucket": "you_bucket_name",
"Key": "filename.yaml"
}
For more see BodyS3Location