Cloud fomation: 2 EIPs, 2PIPs on one ENI - amazon-ec2

Getting "EIP will not stabilize" errors. Code is below... might be the code, or could be cloudformation bug.
I would like to attach 2 EIPs to the interface, one to the primary private IP and one to the secondary private IP. It works when I do it from the console.
I can also add 2 private IPs and a single EIP attached to either the primary or secondary private IP successfully if I comment out the 'VIP' or 'EIP' code. Eithe one works, but not both together.
#ServerOne.
ServerOne:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: !Ref AvailabilityZoneA
DisableApiTermination: !Ref disableInstanceDeletion
ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", ServerOne ]
InstanceType: !FindInMap [ InstanceSizingMap, !Ref StackSizing, ServerOne ]
EbsOptimized: !FindInMap [ InstanceSizingMap, !Ref StackSizing, ebsOptimizedInstances ]
BlockDeviceMappings:
- DeviceName: "/dev/sda1"
Ebs:
DeleteOnTermination: !FindInMap [ InstanceSizingMap, !Ref StackSizing, DeleteOnTermination ]
KeyName: !Ref SSHKeyName
Monitoring: 'false'
NetworkInterfaces:
-
NetworkInterfaceId: !Ref ServerOneInterface
DeviceIndex: 0
Tags:
- Key: Name
Value: ServerOne
- Key: Role
Value: Infrastructure
# ServerOne Server Network. 2 Private IPs, 2 EIPs all on one interface.
ServerOneEIP:
Type: AWS::EC2::EIP
DependsOn: IGW
Properties:
InstanceId: !Ref ServerOne
Domain: vpc
ServerOneVIP:
Type: AWS::EC2::EIP
DependsOn: IGW
Properties:
InstanceId: !Ref ServerOne
Domain: vpc
ServerOneEIPAssociation:
Type: AWS::EC2::EIPAssociation
DependsOn: ServerOneVIPAssociation
Properties:
AllocationId: !GetAtt ServerOneEIP.AllocationId
NetworkInterfaceId: !Ref ServerOneInterface
PrivateIpAddress: !GetAtt ServerOneInterface.PrimaryPrivateIpAddress
ServerOneVIPAssociation:
Type: AWS::EC2::EIPAssociation
DependsOn: IGW
Properties:
AllocationId: !GetAtt ServerOneVIP.AllocationId
NetworkInterfaceId: !Ref ServerOneInterface
PrivateIpAddress: !Select [ 0, !GetAtt ServerOneInterface.SecondaryPrivateIpAddresses ]
ServerOneInterface:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: !Ref PublicSubnetA
SecondaryPrivateIpAddressCount: 1
Description: ServerOne Network Interface
GroupSet: [
!Ref PuppetClientSG ]
# ServerOne is doing NAT, so Source/Dest is false.
SourceDestCheck: false

Code is below.
---
Parameters:
Subnet:
Description: ID of the Subnet the instance should be launched in, this will
link the instance to the same VPC.
Type: List<AWS::EC2::Subnet::Id>
Resources:
EIP1:
Type: AWS::EC2::EIP
Properties:
Domain: VPC
EIP2:
Type: AWS::EC2::EIP
Properties:
Domain: VPC
Association1:
Type: AWS::EC2::EIPAssociation
DependsOn:
- ENI
- EIP1
Properties:
AllocationId:
Fn::GetAtt:
- EIP1
- AllocationId
NetworkInterfaceId:
Ref: ENI
PrivateIpAddress:
Fn::GetAtt:
- ENI
- PrimaryPrivateIpAddress
Association2:
Type: AWS::EC2::EIPAssociation
DependsOn:
- ENI
- EIP2
Properties:
AllocationId:
Fn::GetAtt:
- EIP2
- AllocationId
NetworkInterfaceId:
Ref: ENI
PrivateIpAddress:
Fn::Select:
- '0'
- Fn::GetAtt:
- ENI
- SecondaryPrivateIpAddresses
ENI:
Type: AWS::EC2::NetworkInterface
Properties:
SecondaryPrivateIpAddressCount: 1
SourceDestCheck: false
SubnetId:
Fn::Select:
- '0'
- Ref: Subnet
OpenVPN:
Type: AWS::EC2::Instance
DependsOn:
- ENI
Properties:
InstanceType: t2.micro
AvailabilityZone: us-east-2a
NetworkInterfaces:
- NetworkInterfaceId:
Ref: ENI
DeviceIndex: '0'
ImageId: ami-8a7859ef
KeyName: jimkey

Related

Lambda(Serverless Framework) + DynamoDB Accelerator(DAX): App can not connect

i created my app using AWS(API Gateway/Lambda/DynamoDB).
but loading speed of the app is too late.
so, i want to improve the loading speed using DynamoDB DAX(cache).
serverless.yml
service: myapp
frameworkVersion: '2.31'
provider:
name: aws
runtime: nodejs14.x
lambdaHashingVersion: 20201221
stage: $opt:stage
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dax:*
Resource: '*'
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: '*'
vpc:
securityGroupIds:
- !GetAtt daxSecurityGroup.GroupId
subnetIds:
- !Ref daxSubnet
functions:
graphql:
handler: ./build/src/app.handler
events:
- http:
path: graphql
method: ANY
environment:
DAX_ENDPOINT: !GetAtt daxCluster.ClusterDiscoveryEndpoint
vpc:
securityGroupIds:
- !GetAtt daxSecurityGroup.GroupId
subnetIds:
- !Ref daxSubnet
resources:
Resources:
daxCluster:
Type: AWS::DAX::Cluster
Properties:
ClusterName: dax-cluster
IAMRoleARN: !GetAtt daxRole.Arn
NodeType: dax.t2.small
ReplicationFactor: 1
SecurityGroupIds:
- !GetAtt daxSecurityGroup.GroupId
SubnetGroupName: !Ref daxSubnetGroup
daxRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Principal:
Service:
- dax.amazonaws.com
Version: '2012-10-17'
RoleName: dax-role
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
daxSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security Group for Dax
GroupName: dax-security-group
VpcId: !Ref daxVpc
daxSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
DependsOn: daxSecurityGroup
Properties:
GroupId: !GetAtt daxSecurityGroup.GroupId
IpProtocol: tcp
FromPort: 8111
ToPort: 8111
SourceSecurityGroupId: !GetAtt daxSecurityGroup.GroupId
daxVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
InstanceTenancy: default
Tags:
- Key: Name
Value: dax-cluster
daxSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ''
CidrBlock: 10.0.0.0/20
Tags:
- Key: Name
Value: dax-cluster
VpcId: !Ref daxVpc
daxSubnetGroup:
Type: AWS::DAX::SubnetGroup
Properties:
Description: Subnet group for DAX
SubnetGroupName: dax-subnet-group
SubnetIds:
- !Ref daxSubnet
MyTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:provider.stage}_table
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
PointInTimeRecoverySpecification:
PointInTimeRecoveryEnabled: true
app.ts
import { DynamoDB } from "aws-sdk";
import AmazonDaxClient from "amazon-dax-client";
export default async() {
...
const endpoint = process.env.DAX_ENDPOINT as string;
const config = { ... }
const dax: any = new AmazonDaxClient({endpoints: [endpoint], region: 'us-east-1'});
const dynamodb = new DynamoDB.DocumentClient({...config, service: dax});
await dynamodb.transactWrite({
TransactItems: [
{
Delete: {
TableName: 'development_table',
Key: {
id: args.id,
createdAt: createdAt
}
}
},
],
}).promise();
return ( ... )
}
My App can not connect to API Gateway.(Timeout)
Please help me, That's very kind of you.
i tried...
serverless.yml
...
functions:
graphql:
handler: ./build/src/app.handler
events:
- httpApi:
path: /{id+}
method: GET
- httpApi:
path: /
method: POST
...
But, App can not connect.
i referred to AWS blog
package
"#types/amazon-dax-client": "^1.2.3",
"amazon-dax-client": "^1.2.9",

AWS CloudFormation template not accepting bash script

Trying to put a bash script to install MongoDB on an EC2 via userdata. Im starting simple with this easy script before I do that one I am actually trying to do. I cannot even get the simple one to work. When I try and create the stack with the template below it gives me the error Invalid template property or properties [Properties]. Any ideas whats wrong with my userdata section?
AWSTemplateFormatVersion: 2010-09-09
Description: Launch EC2 via CloudFormation
Parameters:
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: mongodb
AvailabilityZone:
Type: AWS::EC2::AvailabilityZone::Name
VpcCIDR:
Description: Please enter the IP range (CIDR notation) for this VPC
Type: String
Default: 10.0.0.0/16
PublicSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
Type: String
Default: 10.0.0.0/24
PrivateSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: String
Default: 10.0.2.0/24
EnvironmentName:
Description: An environment name that is prefixed to resource names
Type: String
Resources:
496Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-04505e74c0741db8d # ImageID valid only in us-east-1 region
InstanceType: t2.micro
KeyName: !Ref KeyName
SecurityGroupIds:
- !Ref 496SecurityGroup
496SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [496-security-group, dev]]
GroupDescription: "Allow HTTP/HTTPS and SSH inbound and outbound traffic"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref EnvironmentName
- Key: Env
Value: !Ref EnvironmentName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref EnvironmentName
- Key: Env
Value: !Ref EnvironmentName
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PublicSubnetCIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Subnet (AZ1)
- Key: Env
Value: !Ref EnvironmentName
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PrivateSubnetCIDR
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Subnet (AZ1)
- Key: Env
Value: !Ref EnvironmentName
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Routes
- Key: Env
Value: !Ref EnvironmentName
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Routes (AZ1)
- Key: Env
Value: !Ref EnvironmentName
DefaultPrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet
Properties:
UserData: !Base64
Fn::Sub: |
#!/bin/bash
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org
Your UserData is part of AWS::EC2::Instance. It is not a separate resource. Thus it should be:
AWSTemplateFormatVersion: 2010-09-09
Description: Launch EC2 via CloudFormation
Parameters:
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: mongodb
AvailabilityZone:
Type: AWS::EC2::AvailabilityZone::Name
VpcCIDR:
Description: Please enter the IP range (CIDR notation) for this VPC
Type: String
Default: 10.0.0.0/16
PublicSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
Type: String
Default: 10.0.0.0/24
PrivateSubnetCIDR:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: String
Default: 10.0.2.0/24
EnvironmentName:
Description: An environment name that is prefixed to resource names
Type: String
Resources:
496Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-04505e74c0741db8d # ImageID valid only in us-east-1 region
InstanceType: t2.micro
KeyName: !Ref KeyName
SecurityGroupIds:
- !Ref 496SecurityGroup
UserData: !Base64
Fn::Sub: |
#!/bin/bash
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
sudo apt-get update
sudo apt-get install -y mongodb-org
496SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [496-security-group, dev]]
GroupDescription: "Allow HTTP/HTTPS and SSH inbound and outbound traffic"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Ref EnvironmentName
- Key: Env
Value: !Ref EnvironmentName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Ref EnvironmentName
- Key: Env
Value: !Ref EnvironmentName
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PublicSubnetCIDR
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Subnet (AZ1)
- Key: Env
Value: !Ref EnvironmentName
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !Ref PrivateSubnetCIDR
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Subnet (AZ1)
- Key: Env
Value: !Ref EnvironmentName
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Public Routes
- Key: Env
Value: !Ref EnvironmentName
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${EnvironmentName} Private Routes (AZ1)
- Key: Env
Value: !Ref EnvironmentName
DefaultPrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
NatGatewayEIP:
Type: AWS::EC2::EIP
DependsOn: InternetGatewayAttachment
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet

Cloudformation: Invalid permissions on Lambda function found with API GW Integration

This is my cloudformation template, which deploys successfully:
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Deploy Da Blog
Resources:
ArticleTable:
Type: AWS::DynamoDB::Table
Properties:
KeySchema:
- AttributeName: id
KeyType: HASH
AttributeDefinitions:
- AttributeName: id
AttributeType: S
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: !Sub ${AWS::StackName}-Article
UpdateReplacePolicy: Retain
DeletionPolicy: Retain
############################### BLOG API ############################
BlogRestApiLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/apigateway/${AWS::StackName}'
BlogRestApiLoggingRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogRestApiLoggingRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
## specifies the IAM role that Amazon API Gateway uses to write API logs to Amazon CloudWatch Logs
BlogRestApiAccount:
Type: AWS::ApiGateway::Account
Properties:
CloudWatchRoleArn: !GetAtt [ BlogRestApiLoggingRole, Arn ]
BlogRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub '${AWS::StackName}'
Description: Blog API
EndpointConfiguration:
Types:
- EDGE
BlogRestApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId: !Ref BlogRestApi
Description: Automatically created by the RestApi construct
DependsOn:
- BlogRestApi
- BlogApiResource
- BlogApiIdResource
- ListArticlesMethod
- PostArticleMethod
- GetArticleMethod
BlogRestApiStageProd:
Type: AWS::ApiGateway::Stage
DependsOn:
- BlogRestApiDeployment
Properties:
RestApiId: !Ref BlogRestApi
DeploymentId: !Ref BlogRestApiDeployment
StageName: prod
MethodSettings:
- LoggingLevel: INFO
ResourcePath: '/*'
HttpMethod: '*'
MetricsEnabled: true
BlogApiRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub 'BlogApiRole-${AWS::StackName}'
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action:
- sts:AssumeRole
BlogApiPolicy:
Type: AWS::IAM::Policy
DependsOn:
- BlogApiRole
- ListArticlesFunction
- PostArticleFunction
- GetArticleFunction
Properties:
PolicyName: !Sub 'BlogApiPolicy-${AWS::StackName}'
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: 'Allow'
Action: 'lambda:InvokeFunction'
Resource:
- !GetAtt ListArticlesFunction.Arn
- !GetAtt PostArticleFunction.Arn
- !GetAtt GetArticleFunction.Arn
Roles:
- !Ref BlogApiRole
############################### END OF Blog API ############################
############################### START OF Blog Functions ####################
BlogApiResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt [ BlogRestApi, RootResourceId ]
PathPart: article
RestApiId: !Ref BlogRestApi
BlogApiIdResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !Ref BlogApiResource
PathPart: "{id}"
RestApiId: !Ref BlogRestApi
BlogApiOptionsMethod:
Type: AWS::ApiGateway::Method
DependsOn:
- BlogApiResource
Properties:
ApiKeyRequired: true
AuthorizationType: NONE
HttpMethod: OPTIONS
Integration:
Type: MOCK
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': '{"statusCode": 200}'
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'GET,POST,PATCH,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
'application/json': '{}'
MethodResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
ListArticlesFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-list-articles-function'
Handler: com.zenithwebfoundry.blog.api.ListArticlesHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:Query
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
PostArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-post-article-function'
Handler: com.zenithwebfoundry.blog.api.SaveArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:PutItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
PostArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: POST
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PostArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: PostArticle
GetArticleFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: !Sub '${AWS::StackName}-get-article-function'
Handler: com.zenithwebfoundry.blog.api.GetArticleHandler
CodeUri:
Key: !Ref ParamCodePackage
Bucket: !Ref ParamCodeBucket
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- dynamodb:GetItem
Resource: !GetAtt [ ArticleTable, Arn ]
Runtime: java11
Timeout: 10
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref ArticleTable
PRIMARY_KEY: id
DependsOn:
- ArticleTable
GetArticleMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiIdResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetArticleFunction.Arn}/invocations'
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: GetArticle
############################### END OF Blog Functions ######################
BlogAPIDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
DomainName: !Join [ ".", ['blogapi', !Ref DomainName]]
EndpointConfiguration:
Types:
- EDGE
CertificateArn: !Ref DomainCert
SecurityPolicy: TLS_1_0
BlogAPIHostedZone:
Type: AWS::Route53::HostedZone
Properties:
Name: !Ref BlogAPIDomainName
BlogAPIBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref BlogAPIDomainName
RestApiId: !Ref BlogRestApi
Stage: 'prod'
Route53RecordSetGroup:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneId: !Ref DomainHostedZoneId
RecordSets:
- Name: !Join [ "", ['blog', '.', !Ref DomainName, '.']]
Type: A
TTL: '300'
ResourceRecords:
- 52.64.238.177
- Name: !Join [ ".", ['blogapi', !Ref DomainName]]
Type: A
AliasTarget:
# HostedZoneId: !Ref BlogAPIHostedZone # distributionHostedZoneId - alias target name does not lie in the target zone
HostedZoneId: Z2FDTNDATAQYW2
# https://j97h8bvml9.execute-api.ap-southeast-2.amazonaws.com/prod/articles
DNSName: !GetAtt [BlogAPIDomainName, DistributionDomainName]
AssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'assets' ] ]
CorsConfiguration:
CorsRules:
- AllowedHeaders: ['*']
AllowedMethods: [GET,PUT,POST,DELETE,HEAD]
AllowedOrigins: ['http://localhost*']
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join [ ".", [ !Ref 'AWS::StackName', 'web' ] ]
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebsiteConfiguration:
IndexDocument: 'index.html'
ErrorDocument: 'index.html'
Parameters:
ParamCodePackage:
Type: String
ParamCodeBucket:
Type: String
DomainName:
Description: "Public DNS Zone Name"
Type: String
DomainHostedZoneId:
Type: String
Description: 'The AWS HostedZoneId of the above domain name'
DomainCert:
Type: String
Description: 'The arn reference to the certificate used with the domain.'
ParamRequestMappingTemplate:
Type: String
Description: 'Read from resources/templates'
SaveArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.SaveArticleHandler'
GetArticleHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.GetArticleHandler'
ListArticlesHandler:
Type: String
Default: 'com.zenithwebfoundry.blog.api.ListArticlesHandler'
ParamAPIGatewayRequestEventMappingTemplate:
Type: String
Default: '{
"resource" : "$context.resourceId",
"path" : "$context.path",
"httpMethod" : "$context.httpMethod",
"headers": {
#foreach($header in $input.params().header.keySet())
"$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end
#end
},
"method": "$context.httpMethod",
"pathParameters": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
},
"queryStringParameters": {
#foreach($queryParam in $input.params().querystring.keySet())
"$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end
#end
},
"body" : $input.json("$"),
"isBase64Encoded": false
}'
ParamAPIGatewayResponseEventMappingTemplate:
Type: String
Default: '#set($statusCode = $input.path("$.statusCode"))
#set($context.responseOverride.status = $statusCode)
#set($headers = $input.path("$.headers"))
#foreach($key in $headers.keySet())
#set($context.responseOverride.header[$key] = $headers[$key])
#end
#set($context.responseOverride.header.Access-Control-Allow-Headers = "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token")
#set($context.responseOverride.header.Access-Control-Allow-Methods = "*")
#set($context.responseOverride.header.Access-Control-Allow-Origin = "*")
{
"body": $input.json("$.body")
}
'
Outputs:
ArticleEndpoint:
Value: !Join ["", ['https://', !Ref BlogRestApi, '.execute-api.ap-southeast-2.', !Ref 'AWS::URLSuffix', '/', !Ref BlogRestApiStageProd, '/'] ]
The problem is that when I try to call the ListArticlesFunction using the following curl expression:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET https://blogapi.zenithwebfoundry.com/article
I get the following:
HTTP/2 500
content-type: application/json
content-length: 36
date: Sun, 30 Aug 2020 09:09:50 GMT
x-amzn-requestid: c1948ed5-18c5-4807-8904-3c1c2af06c25
x-amzn-errortype: InternalServerErrorException
x-amz-apigw-id: SE3y1G71ywMF5fw=
x-cache: Error from cloudfront
via: 1.1 647846f53eba457a8e4ba1d1d42a6336.cloudfront.net (CloudFront)
x-amz-cf-pop: SYD1-C1
x-amz-cf-id: 6bnMhNRUdf1znTmD0vQn86UZcMF_j9JCzFb-JvhwwT9j6ch4P8t20g==
{"message": "Internal server error"}
Checking cloudwatch, I see the slightly cryptic error:
Execution failed due to configuration error: Invalid permissions on Lambda function
The same thing happens if I go into the APIGateway service console and go to the article GET resource. So at least its consistent.
I have tested the handler in isolation and there's no compilation problems anywhere, so I'm pretty sure its stack-related.
I gather that the problem must either be in the BlogApiPolicy Policy or the Lambda Policy, but nothing I set seems to work. Does anyone know what cloudformation shenanigans is needed to get this right?
The answer is that I needed the role added as a Credentials: entry in the method of each Lambda, for example:
ListArticlesMethod:
Type: AWS::ApiGateway::Method
Properties:
HttpMethod: GET
ResourceId: !Ref BlogApiResource
RestApiId: !Ref BlogRestApi
AuthorizationType: NONE
Integration:
IntegrationHttpMethod: POST
Type: AWS
Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ListArticlesFunction.Arn}/invocations'
Credentials: !GetAtt [BlogApiRole, Arn]
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
'application/json': !Ref ParamAPIGatewayRequestEventMappingTemplate
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
'application/json': !Ref ParamAPIGatewayResponseEventMappingTemplate
MethodResponses:
- StatusCode: 200
OperationName: ListArticles
Why this is suddenly needed, when this was never needed before, is quite beyond me, but I must thank #Marcin for his/her patient assistance and convincing my doubting mind.
Based on the comments.
The issue was that BlogApiRole, although being created, it was not used in any of the API Gateway methods. This is required, because API Gateway needs to have explicit permissions to invoke a lambda functions. To enable this, Credentials in AWS::ApiGateway::Method Integration should be set.
The alternative is to use AWS::Lambda::Permission. The use of the AWS::Lambda::Permission is especially useful when there are many methods in the API gateway which require invoking the lambda. The reason is that you can create one such AWS::Lambda::Permission for the lambda function which can allow API gateway access to the lambda as a whole. This saves us from defining Credentials for each method independently.

How to have a public APIG access a lambda inside a VPC?

I have a small web app deployed as an aws lambda that correctly operates when tested via the lambda management console. However running a test via the apig console yeilds this error:
Sun Jan 26 21:43:30 UTC 2020 : Execution failed due to configuration error: Invalid permissions on Lambda function
However the apig is provisioned with the execute permission:
apiGatewayRole:
DependsOn:
- squashApp
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "apigateway.amazonaws.com"
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "API_Service_Role_Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action: "lambda:InvokeFunction"
Resource: !GetAtt "squashApp.Arn"
Effect: "Allow"
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/"
Previously the permission was sufficient (The IAM policy is recently added) to permit the lambda invocation. The error started occuring after I moved the lambda inside a VPC.
squashApp:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
GO_ENV: production
DB_HOST: !GetAtt "squashRDS.Endpoint.Address"
DB_PORT: !GetAtt "squashRDS.Endpoint.Port"
DB_USER_NAME: !Sub "${rdsUser}"
DB_USER_PASS: !Sub "${rdsPassword}"
Code:
S3Bucket: '${lambdaHandoffBucket}'
S3Key: !Sub 'functions/${lambdaVersion}/${lambdaHash}' # TODO make parameter
Handler: 'app'
Role: !GetAtt "squashLambdaRole.Arn"
Runtime: 'go1.x'
Timeout: 2
FunctionName: !Ref "lambdaFunctionName"
VpcConfig:
SecurityGroupIds:
- !Ref "lambdaSecurtiyGroup"
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.192.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
privateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.21.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "PrimaryAZ"
Tags:
- Key: Name
Value: !Sub ${apiGatewayStageName} Private Subnet (AZ1)
privateSubnet2:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.30.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "SecondaryAZ"
privateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
privateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
noIngressSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "no-ingress-sg"
GroupDescription: "Security group with no ingress rule"
VpcId: !Ref vpc
The full cloud formation resource section:
Resources:
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "SquashAPI"
Description: "Squash Box Ladder"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
# This can be ANY | GET| POST | etc and references the api http method as seen by a client
HttpMethod: "ANY"
Integration:
# This must be post or the integration between gateway and lambda fails
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "squashApp.Arn"
ResourceId: !GetAtt "apiGateway.RootResourceId"
RestApiId: !Ref "apiGateway"
apiGatewayCatchAllProxy:
Type: AWS::ApiGateway::Resource
Properties:
PathPart: "{proxy+}"
RestApiId: !Ref "apiGateway"
ParentId: !GetAtt "apiGateway.RootResourceId"
apiGatewayCatchAllMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "ANY"
Integration:
# This must be post or the integration between gateway and lambda fails
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "squashApp.Arn"
ResourceId: !Ref "apiGatewayCatchAllProxy"
RestApiId: !Ref "apiGateway"
apiGatewayDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- "apiGatewayRootMethod"
- "apiGatewayCatchAllMethod"
Properties:
RestApiId: !Ref "apiGateway"
StageName: !Ref "apiGatewayStageName"
squashApp:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
GO_ENV: production
DB_HOST: !GetAtt "squashRDS.Endpoint.Address"
DB_PORT: !GetAtt "squashRDS.Endpoint.Port"
DB_USER_NAME: !Sub "${rdsUser}"
DB_USER_PASS: !Sub "${rdsPassword}"
Code:
S3Bucket: '${lambdaHandoffBucket}'
S3Key: !Sub 'functions/${lambdaVersion}/${lambdaHash}' # TODO make parameter
Handler: 'app'
Role: !GetAtt "squashLambdaRole.Arn"
Runtime: 'go1.x'
Timeout: 2
FunctionName: !Ref "lambdaFunctionName"
VpcConfig:
SecurityGroupIds:
- !Ref "lambdaSecurtiyGroup"
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
lambdaSecurtiyGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: For Lambda Application
VpcId: !Ref vpc
lambdaEgres:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref lambdaSecurtiyGroup
IpProtocol: tcp
FromPort: !GetAtt "squashRDS.Endpoint.Port"
ToPort: !GetAtt "squashRDS.Endpoint.Port"
CidrIp: !GetAtt 'vpc.CidrBlock'
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/"
squashLambdaRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*"
PolicyName: "lambda"
lambdaLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/aws/lambda/${lambdaFunctionName}"
RetentionInDays: 14
apiGatewayRole:
DependsOn:
- squashApp
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "apigateway.amazonaws.com"
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "API_Service_Role_Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action: "lambda:InvokeFunction"
Resource: !GetAtt "squashApp.Arn"
Effect: "Allow"
apiGwAccountConfig:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn: !GetAtt "apiGatewayRole.Arn"
squashRDS:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.t2.micro
AllocatedStorage: 20
StorageType: "gp2"
Engine: "postgres"
MasterUsername: !Sub "${rdsUser}"
MasterUserPassword: !Sub "${rdsPassword}"
DBSubnetGroupName: !Ref "rdsSubnetGroup"
VPCSecurityGroups:
- !Ref rdsSecurityGroup
DeletionPolicy: Snapshot
rdsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: For RDS Instance
VpcId: !Ref vpc
Tags:
- Key: Name
Value: RDS-SecurityGroup
rdsSubnetGroup:
Type: "AWS::RDS::DBSubnetGroup"
Properties:
DBSubnetGroupName: "RDS Subnet Group"
DBSubnetGroupDescription: Subnet including the RDS into the VPC
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
rdsIngres:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref rdsSecurityGroup
IpProtocol: tcp
FromPort: !GetAtt "squashRDS.Endpoint.Port"
ToPort: !GetAtt "squashRDS.Endpoint.Port"
CidrIp: !GetAtt 'vpc.CidrBlock'
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.192.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
privateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.21.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "PrimaryAZ"
privateSubnet2:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.30.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "SecondaryAZ"
privateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
privateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
noIngressSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "no-ingress-sg"
GroupDescription: "Security group with no ingress rule"
VpcId: !Ref vpc
Outputs:
apiGatewayInvokeURL:
Value: !Sub "https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}"
lambdaArn:
Value: !GetAtt "squashApp.Arn"
In "squashLambdaRole", try adding "apigateway.amazonaws.com" in service principal list + attach a AWS managed policy named "AWSLambdaRole".
It solved my problem but I did not understand why :/
The only thing I can think of is, the API gateway source Arn should end with a resource path. In your case, it ends with a /.
I would add an * at the end to allow any resource path to invoke the lambda. You can change it to a specific path once you get it working.
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/*"
hope this helps. good luck.

Getting Error : Circular dependency between resources

I'm getting circular dependency errors for my CloudFormation code:
Circular dependency between resources: [WebServerScaleDownPolicy,
WebServerScaleUpPolicy, LaunchConfig, ElasticLoadBalancer,
CPUAlarmHigh, MySecurityGroup, CPUAlarmLow, WebServerGroup]
Code File : https://drive.google.com/open?id=1SxDqc4oPRW0SgjtDg3eoVN_YE01iRLD1
I tried adding "DependsOn", but that doesn't help. I'm new to CloudFormation and looking for help.
AWSTemplateFormatVersion: '2010-09-09'
# this is the CloudFormation template deploys a Vpc
Resources:
VPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: '10.0.0.0/16'
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: myWebsite-VPC
InternetGateway:
Type: 'AWS::EC2::InternetGateway'
Properties:
Tags:
- Key: Name
Value: myWebsite-IGW
VPCGatewayAttachment:
Type: 'AWS::EC2::VPCGatewayAttachment'
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
SubnetAPublic:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone: us-east-1a
CidrBlock: '10.0.1.0/24'
MapPublicIpOnLaunch: Yes
VpcId: !Ref VPC
Tags:
- Key: name
Value: 'A public'
- Key: Reach
Value: Public
SubnetBPublic:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone: us-east-1b
CidrBlock: '10.0.2.0/24'
MapPublicIpOnLaunch: Yes
VpcId: !Ref VPC
Tags:
- Key: Name
Value: 'B public'
- Key: Reach
Value: Public
RouteTablePublic:
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: Public-routeTable
RouteTableAssociationAPublic:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetAPublic
RouteTableId: !Ref RouteTablePublic
RouteTableAssociationBPublic:
Type: 'AWS::EC2::SubnetRouteTableAssociation'
Properties:
SubnetId: !Ref SubnetBPublic
RouteTableId: !Ref RouteTablePublic
RouteTablePublicInternetRoute:
Type: 'AWS::EC2::Route'
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref RouteTablePublic
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
WebServerGroup:
Type: 'AWS::AutoScaling::AutoScalingGroup'
Properties:
AutoScalingGroupName: myASG
Cooldown: 200
DesiredCapacity: 2
LaunchConfigurationName: !Ref LaunchConfig
MaxSize: 6
MinSize: 2
LoadBalancerNames: [!Ref 'ElasticLoadBalancer']
HealthCheckGracePeriod: 300
Subnets:
- us-east-1a
- us-east-1b
LaunchConfig:
Type: AWS::AutoScaling::LaunchConfiguration
Metadata:
Comment: Install Apache
AWS::CloudFormation::Init:
config:
packages:
yum:
httpd: []
files:
/var/www/html/index.html:
content: "<html><h1>this is my webpage</h1></html>"
mode: '000644'
owner: root
group: root
services:
sysvinit:
httpd:
enabled: "true"
ensureRunning: "true"
Properties:
KeyName: MYEC2Keypair
SecurityGroup: !Ref MySecurityGroup
InstanceType: t2.micro
ImageId: ami-0b898040803850657
WebServerScaleUpPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: !Ref WebServerGroup
Cooldown: '60'
ScalingAdjustment: 1
WebServerScaleDownPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AdjustmentType: ChangeInCapacity
AutoScalingGroupName: !Ref WebServerGroup
Cooldown: '60'
ScalingAdjustment: -1
CPUAlarmHigh:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Scale-up if CPU > 90% for 10 minutes
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: 300
EvaluationPerioods: 2
Threshold: 90
AlarmActions: [!Ref 'WebServerScaleUpPolicy']
Dimensions:
- Name: AutoScalingGroupName
Value: !Ref WebServerGroup
ComparisonOperator: GreaterThanThreshold
CPUAlarmLow:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Scale-down if CPU < 70% for 10 minutes
MetricName: CPUUtilization
Namespace: AWS/EC2
Statistic: Average
Period: 300
EvaluationPeriods: 2
Threshold: 70
AlarmActions: [!Ref 'WebServerScaleDownPolicy']
Dimensions:
- Name: AutoScalingGroupName
Value: !Ref WebServerGroup
ComparisonOperator: LessThanThreshold
ElasticLoadBalancer:
Type: 'AWS::ElasticLoadBalancing::LoadBalancer'
DependsOn: MySecurityGroup
Properties:
VpcId: !Ref VPC
CrossZone: 'true'
Listeners:
- LoadBalancerPort: '80'
InstancePort: '80'
Protocol: HTTP
SecurityGroup: !Ref MySecurityGroup
AvailabilityZones:
- us-east-1a
- us-east-1b
HealthCheck:
Target: HTTP:80/
HealthyThreshold: '3'
UnhealthyThreshold: '5'
Interval: '30'
Timeout: '5'
Tags:
- Key: Name
Value: MyELB
MySecurityGroup:
Type: AWS::EC2::SecruityGroup
Properties:
GroupDescription: Allow http and ssh only from LoadBalancer
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: '0.0.0.0/0'
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: '0.0.0.0/0'
SourceSecurityGroupOwnerId: !GetAtt [ElasticLoadBalancer, SourceSecurityGroup.OwnerAlias]
SourceSecurityGroupName: !GetAtt [ElasticLoadBalancer, SourceSecurityGroup.GroupName]
Tags:
- Key: Name
Value: MySecurityGroup
This is because your "ElasticLoadBalancer" resource depends on your security group "MySecurityGroup" and MySecurityGroup is referencing the Load Balancer in it's ingress rule, that's why there is a circular dependency.
You should define a separate security group for your load balancer and then in "MySecurityGroup" allow ssh and and http from that security group.
The CloudFormation Linter will warn about circular dependencies and more:
E3012 Property Resources/WebServerGroup/Properties/Cooldown should be of type String
~/Downloads/template.yml:76:7
E3012 Property Resources/WebServerGroup/Properties/DesiredCapacity should be of type String
~/Downloads/template.yml:77:7
E3012 Property Resources/WebServerGroup/Properties/MaxSize should be of type String
~/Downloads/template.yml:79:7
E3012 Property Resources/WebServerGroup/Properties/MinSize should be of type String
~/Downloads/template.yml:80:7
E3002 Invalid Property Resources/WebServerGroup/Properties/Subnets
~/Downloads/template.yml:83:7
E3002 Invalid Property Resources/LaunchConfig/Properties/SecurityGroup
~/Downloads/template.yml:108:7
E3003 Property EvaluationPeriods missing at Resources/CPUAlarmHigh/Properties
~/Downloads/template.yml:127:5
E3002 Invalid Property Resources/CPUAlarmHigh/Properties/EvaluationPerioods
~/Downloads/template.yml:133:7
E3004 Circular Dependencies for resource ElasticLoadBalancer. Circular dependency with [MySecurityGroup, ElasticLoadBalancer]
~/Downloads/template.yml:155:3
E3002 Invalid Property Resources/ElasticLoadBalancer/Properties/VpcId
~/Downloads/template.yml:159:7
E3012 Property Resources/ElasticLoadBalancer/Properties/CrossZone should be of type Boolean
~/Downloads/template.yml:160:7
E3002 Invalid Property Resources/ElasticLoadBalancer/Properties/SecurityGroup
~/Downloads/template.yml:165:7
E3002 Invalid Property Resources/ElasticLoadBalancer/Properties/HealthCheck/Tags
~/Downloads/template.yml:175:9
E3004 Circular Dependencies for resource MySecurityGroup. Circular dependency with [MySecurityGroup, ElasticLoadBalancer]
~/Downloads/template.yml:178:3
E3001 Invalid or unsupported Type AWS::EC2::SecruityGroup for resource MySecurityGroup in us-east-1
~/Downloads/template.yml:179:5
DependsOn won't solve circular dependencies.
Would it be possible to remove DependsOn: MySecurityGroup from ElasticLoadBalancer and choose the value of properties SourceSecurityGroupOwnerId and SourceSecurityGroupName in MySecurityGroup without depending on ElasticLoadBalancer? Those two properties may not be required if you'd rather not specify it at all for one of those properties.
Those options would solve one of the circular dependencies.

Resources