CloudFormation - Using - >- expression with !Sub - amazon-web-services

Can I use - >- with !Sub?
Properties:
DashboardBody: !Sub
- >-
'{
... too long json ...
'}
- {something else}
Will it work?

Yes, it will work. Here is an example:
Resources:
MyDashBoard:
Type: AWS::CloudWatch::Dashboard
Properties:
DashboardBody:
!Sub
- >-
{
"widgets": [
{
"type": "metric",
"width": 12,
"height": 6,
"properties": {
"metrics": [
["AWS/EC2","CPUUtilization","AutoScalingGroupName","<your-asg-name>"]
],
"region": "${AWS::Region}",
"period": 60,
"title": "${title}"
}
}
]
}
- title: "my-dasboard-title"
DashboardName: my-dashboard-name

Related

aws cloudformation template for spot request in ec2 instance

I need a sample cloud formation template to add spot requests while provisioning the ec2 instance in AWS.I have tried with console to provision spot instances but I couldn't find any exact template for add spot request in ec2
You Can create a SpotFleet resource, here's an example
SpotFleet:
Type: AWS::EC2::SpotFleet
Properties:
SpotFleetRequestConfigData:
IamFleetRole: !GetAtt [IAMFleetRole, Arn]
SpotPrice: '1000'
TargetCapacity:
Ref: TargetCapacity
LaunchSpecifications:
- EbsOptimized: 'false'
InstanceType:
Ref: InstanceType
ImageId:
Fn::FindInMap:
- AWSRegionArch2AMI
- Ref: AWS::Region
- Fn::FindInMap:
- AWSInstanceType2Arch
- Ref: InstanceType
- Arch
SubnetId:
Ref: Subnet1
WeightedCapacity: '8'
- EbsOptimized: 'true'
InstanceType:
Ref: InstanceType
ImageId:
Fn::FindInMap:
- AWSRegionArch2AMI
- Ref: AWS::Region
- Fn::FindInMap:
- AWSInstanceType2Arch
- Ref: InstanceType
- Arch
Monitoring:
Enabled: 'true'
SecurityGroups:
- GroupId:
Fn::GetAtt:
- SG0
- GroupId
SubnetId:
Ref: Subnet0
IamInstanceProfile:
Arn:
Fn::GetAtt:
- RootInstanceProfile
- Arn
WeightedCapacity: '8'
You need to create Spot-fleet resource.
Example :
"SpotFleet": {
"Type": "AWS::EC2::SpotFleet",
"Properties": {
"SpotFleetRequestConfigData": {
"IamFleetRole": { "Fn::GetAtt": [ "IAMFleetRole", "Arn"] },
"SpotPrice": "1000",
"TargetCapacity": { "Ref": "TargetCapacity" },
"LaunchSpecifications": [
{
"EbsOptimized": "false",
"InstanceType": { "Ref": "InstanceType" },
"ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" },
{ "Fn::FindInMap": [ "AWSInstanceType2Arch", { "Ref": "InstanceType" }, "Arch" ] }
]},
"SubnetId": { "Ref": "Subnet1" },
"WeightedCapacity": "8"
},
{
"EbsOptimized": "true",
"InstanceType": { "Ref": "InstanceType" },
"ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" },
{ "Fn::FindInMap": [ "AWSInstanceType2Arch", { "Ref": "InstanceType" }, "Arch" ] }
]},
"Monitoring": { "Enabled": "true" },
"SecurityGroups": [ { "GroupId": { "Fn::GetAtt": [ "SG0", "GroupId" ] } } ],
"SubnetId": { "Ref": "Subnet0" },
"IamInstanceProfile": { "Arn": { "Fn::GetAtt": [ "RootInstanceProfile", "Arn" ] } },
"WeightedCapacity": "8"
}
]
}
}
}
More details can be found in this link :
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-spotfleet.html

Cloudformation DynamoDB-API Gateway Proxy

I was trying to implement a dynamodb proxy using apigateway. But when invoking it, the api is returning error,
Fri Mar 19 20:30:27 UTC 2021 : Execution failed due to configuration
error: Unable to transform request
Fri Mar 19 20:30:27 UTC 2021 : Method completed with status: 500
To me it looks like the issue is not with the requestTemplates transformation(?), but what else, any idea?
Cloudformation template.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Feedback Store; A hypothetical example to test direct proxy to Dynamodb table from api gateway.
Metadata:
cfn-lint:
config:
ignore_checks:
# Not useful at all.
- I1022
Parameters:
Environment:
Type: String
Description: Current environment
Default: "dev"
Resources:
EventsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: feedback_events
AttributeDefinitions:
- AttributeName: UserID
AttributeType: S
- AttributeName: TimeUTC
AttributeType: S
KeySchema:
- AttributeName: UserID
KeyType: HASH
- AttributeName: TimeUTC
KeyType: RANGE
BillingMode: PAY_PER_REQUEST
Tags:
- Key: "Feedbacks"
Value: "true"
ApiGatewayDynamoRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service: "apigateway.amazonaws.com"
Action:
- "sts:AssumeRole"
Policies:
- PolicyName: "ApiGatewayDynamoRolePolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "dynamodb:DescribeTable"
- "dynamodb:BatchWriteItem"
- "dynamodb:PutItem"
- "dynamodb:Query"
- "dynamodb:UpdateItem"
Resource:
- !GetAtt EventsTable.Arn
- Effect: "Allow"
Action:
- "dynamodb:ListTables"
Resource: "*"
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
DefinitionBody:
swagger: 2.0
info:
title: !Sub "${AWS::StackName}-backend-api"
version: 2021-03-21T22:37:24Z
basePath: /prod
schemes:
- https
paths:
/feedback:
post:
summary: Creates a new feedback
description: |
Creates a new feedback object in the datastore
consumes:
- application/json
produces:
- application/json
parameters:
- name: NewFeedback
in: body
description: New feedback details.
schema:
$ref: "#/definitions/Feedbacks"
tags:
- Feedback Store
x-amazon-apigateway-integration:
type: aws
uri: !Sub arn:aws:apigateway:${AWS::Region}:dynamodb:action/BatchWriteItem
credentials: !GetAtt ApiGatewayDynamoRole.Arn
httpMethod: POST
requestTemplates:
application/json: |
#set($inputRoot = $input.path('$'))
{
"RequestItems": {
"${self:resources.Resources.EventsTable.Properties.TableName}": [
#foreach($event in $inputRoot.event)
{
"PutRequest": {
"Item": {
"UserID" : { "S": "$event.userid" },
"TimeUTC" : { "S": "$event.time" },
"Lat": { "S": "$event.lat" },
"Lng": { "S": "$event.lng" },
"UUID" : { "S": "$event.uuid"},
"Sensor" : { "S": "$event.sensor_name" },
"Reading" : { "S": "$event.reading_value" },
"Active" : { "S": "$event.is_active" },
}
}
}#if($foreach.hasNext),#end
#end
]
},
"ReturnValues": "UPDATED_NEW",
"ReturnConsumedCapacity": "NONE",
"ReturnItemCollectionMetrics": "NONE"
}
responses:
"default":
statusCode: "200"
SelectionPattern: "2\\d{2}"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
"BAD.*":
statusCode: "400"
SelectionPattern: "4\\d{2}"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
"INT.*":
statusCode: "500"
SelectionPattern: "5\\d{2}"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responses:
"200":
description: The unique identifier of the new feedback
headers:
Access-Control-Allow-Origin:
type: "string"
Access-Control-Allow-Methods:
type: string
Access-Control-Allow-Headers:
type: string
schema:
$ref: "#/definitions/NewFeedbackResponse"
"400":
description: Bad request
headers:
Access-Control-Allow-Origin:
type: "string"
Access-Control-Allow-Methods:
type: string
Access-Control-Allow-Headers:
type: string
schema:
$ref: "#/definitions/Error"
"500":
description: Internal error
headers:
Access-Control-Allow-Origin:
type: "string"
Access-Control-Allow-Methods:
type: string
Access-Control-Allow-Headers:
type: string
schema:
$ref: "#/definitions/Error"
definitions:
Empty:
type: object
title: Empty Schema
Feedbacks:
type: array
items:
$ref: FeedbackItem
FeedbackItem:
properties:
userid:
type: string
description: UserID of the author
time:
type: string
description: Feedback time
lat:
type: string
description: Latitude
lng:
type: string
description: Longitude
uuid:
type: string
description: Globaly unique id for the feedback
sensor_name:
type: string
description: Device the feedback coming from
reading_value:
type: string
description: Current reading
is_active:
type: string
description: Device status
NewFeedbackResponse:
properties:
feedbackId:
type: string
description: The generated unique identifier for the new feedback
Error:
properties:
code:
type: integer
format: int32
message:
type: string
fields:
type: string
StageName: prod
Variables:
StageVariableName: "TestAPI"
Payload:
{
"event": [
{
"userid": "21d6523137f6",
"time": "2020-06-16T15:22:33Z",
"lng": "-122.03053391",
"lat": "37.33180957",
"uuid": "96a6f48c-fe67-4cad-be24-21d6523137f6",
"sensor_name": "CYT523",
"reading_value": "72.9",
"is_active": "true"
},
{
"userid": "4354069ba6e5",
"time": "2020-06-16T15:22:33Z",
"lng": "-122.03053391",
"lat": "37.33180957",
"uuid": "512f2543-a458-424c-a141-4354069ba6e5",
"sensor_name": "JQR928",
"reading_value": "41.3",
"is_active": "true"
}
]
}
Three things I noticed which needs corrections.
Dynamo table name is not replaced correctly. We need to use !Sub with tableName: !Ref EventsTable
There is a comma after "Active" : { "S": "$event.is_active" } which shouldn't be there.
The three additional parameters are not all valid at root level in batch-write-item api, I removed them temporarily, we can add as needed. "ReturnValues": "UPDATED_NEW", "ReturnConsumedCapacity": "NONE", and "ReturnItemCollectionMetrics": "NONE"
Updated request template:
requestTemplates:
application/json: !Sub
- |
#set($inputRoot = $input.path('$'))
{
"RequestItems": {
"${tableName}": [
#foreach($event in $inputRoot.event)
{
"PutRequest": {
"Item": {
"UserID" : { "S": "$event.userid" },
"TimeUTC" : { "S": "$event.time" },
"Lat": { "S": "$event.lat" },
"Lng": { "S": "$event.lng" },
"UUID" : { "S": "$event.uuid"},
"Sensor" : { "S": "$event.sensor_name" },
"Reading" : { "S": "$event.reading_value" },
"Active" : { "S": "$event.is_active" }
}
}
}#if($foreach.hasNext),#end
#end
]
},
"ReturnValues": "UPDATED_NEW",
"ReturnConsumedCapacity": "NONE",
"ReturnItemCollectionMetrics": "NONE"
}
- {
tableName: !Ref EventsTable
}
I think this line is the issue:
"${self:resources.Resources.EventsTable.Properties.TableName}"
I am not sure that works in API Gateway VTL. My recommendation is to get it working with static code, then add the refs back in.

How to create cloudwatch dash board for step function using cloudformation templates

I am trying to create a cloud watch dash board for a state machine step function using cloud formation template.
The stack creation is getting failed due to the error "The field DashboardBody must be a valid JSON object".
Any idea how can i pass the log group name taken from the parameter section of the template?
below is my cloud formation template:
AWSTemplateFormatVersion: 2010-09-09
Description: >-
CloudFormation template to create a dashboard for state functions
Parameters:
DashboardName:
Description: Name of the dashboard to create
Type: String
Default: Test-board
LambdaFunction:
Description: The Lambda function name.
Type: String
Default: test-lambda
StepFunction:
Description: The state function name.
Type: String
Default: MyStateMachine
Resources:
MetricsDashboard:
Type: 'AWS::CloudWatch::Dashboard'
Properties:
DashboardName: !Ref DashboardName
DashboardBody: !Join
- ''
- - >-
{ "widgets": [
{ "type": "log", "x": 6, "y": 6, "width": 18, "height": 3,
"properties": {
"query": "
- !Join
- ""
- "SOURCE '/aws/states/"
- ""
- !Ref StepFunction
- ""
- "' | fields #message\r\n| filter #message like /ERROR/| filter #message like /Exception/ "
- >-
",
"region": "eu-west-1", "stacked": false, "title": "{ Ref:StepFunction},
Step Function Error Logs", "view": "table" } },
{
"type": "metric",
"x": 0,
"y": 0,
"width": 5,
"height": 5,
"properties": {
"view": "singleValue",
"metrics": [
[ "AWS/States", "ExecutionsFailed", "StateMachineArn", "',{ Ref: StepFunction },'" ],
[ ".", "ExecutionsSucceeded", ".", ".", { "color": "#2ca02c" } ],
[ ".", "ExecutionsAborted", ".", ".", { "visible": false, "color": "#ff9896" } ]
],
"stat": "Sum",
"setPeriodToTimeRange": true,
"region": "', { Ref: AWS::Region }, '",
"title": "', "State-Function-", { Ref: StepFunction }," Counts", '",
"period": 300
}
}
]
}
Thanks in advance,
Remove the joins, they are ending up as part of the json of the final dashboard.
See if this works for you:
AWSTemplateFormatVersion: 2010-09-09
Description: >-
CloudFormation template to create a dashboard for state functions
Parameters:
DashboardName:
Description: Name of the dashboard to create
Type: String
Default: Test-board
LambdaFunction:
Description: The Lambda function name.
Type: String
Default: test-lambda
StepFunction:
Description: The state function name.
Type: String
Default: MyStateMachine
Resources:
MetricsDashboard:
Type: 'AWS::CloudWatch::Dashboard'
Properties:
DashboardName: !Ref DashboardName
DashboardBody: !Sub |
{
"widgets": [
{
"type": "log",
"x": 6,
"y": 6,
"width": 18,
"height": 3,
"properties": {
"query": "SOURCE '/aws/states/${StepFunction}' | fields #message\r\n| filter #message like /ERROR/| filter #message like /Exception/ ",
"region": "eu-west-1",
"stacked": false,
"title": "${StepFunction} Step Function Error Logs",
"view": "table"
}
},
{
"type": "metric",
"x": 0,
"y": 0,
"width": 5,
"height": 5,
"properties": {
"view": "singleValue",
"metrics": [
[
"AWS/States",
"ExecutionsFailed",
"StateMachineArn",
"${StepFunction}"
],
[
".",
"ExecutionsSucceeded",
".",
".",
{
"color": "#2ca02c"
}
],
[
".",
"ExecutionsAborted",
".",
".",
{
"visible": false,
"color": "#ff9896"
}
]
],
"stat": "Sum",
"setPeriodToTimeRange": true,
"region": "${AWS::Region}",
"title": "State Function ${StepFunction} Counts",
"period": 300
}
}
]
}

Invalid template resource property 'ElbDomainName'

For the below cloudformation template:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Todobackend Stack"
# Stack Parameters
Parameters:
VpcId:
Type: "AWS::EC2::VPC::Id"
Description: "The target VPC Id"
SubnetId:
Type: "AWS::EC2::Subnet::Id"
Description: "The target Subnet Id in availability zone a"
KeyPair:
Type: "String"
Description: "The key pair that is allowed SSH access"
InstanceCount:
Type: "Number"
Description: "The desired number of application instances"
DbSubnets:
Type: "List<AWS::EC2::Subnet::Id>"
Description: "The target DB Subnet Group subnet Ids"
DbAvailabilityZone:
Type: "String"
Description: "The target availability zone for the database instance"
DbUsername:
Type: "String"
Description: "The RDS database username"
DbPassword:
Type: "String"
Description: "The RDS database password"
NoEcho: "true"
# Stack Resources
Resources:
# Configure auto scaing group
AutoScalingGroup:
Type: "AWS::AutoScaling::AutoScalingGroup"
Properties:
VPCZoneIdentifier: [ { "Ref": "SubnetId" } ]
LaunchConfigurationName: { "Ref": "AutoScalingLaunchConfiguration" }
MinSize: 0
MaxSize: 2
DesiredCapacity: { "Ref": "InstanceCount" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, " -instance" ] ] }
PropagateAtLaunch: "true"
AutoScalingLaunchConfiguration:
Type: "AWS::AutoScaling::LaunchConfiguration"
Properties:
ImageId: ami-05958d7635caa4d04
InstanceType: t2.micro
keyName: { "Ref": "KeyPair" }
IamInstanceProfile: { "Ref": "EC2InstanceProfile" }
SecurityGroups:
- { "Ref": "EC2InstanceSecurityGroup" }
UserData: {
"Fn::Base64": { "Fn::Join": ["", [
"#!/bin/bash\n",
"echo ECS_CLUSTER=", { "Ref": "EcsCluster"}, " >> /etc/ecs/ecs.config\n"
] ] }
}
EC2InstanceSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "todobackend-sg"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "8080"
ToPort: "8080"
SourceSecurityGroupId: { "Ref": "ElbSecurityGroup" }
- IpProtocol: "tcp"
FromPort: "22"
ToPort: "22"
CidrIp: "0.0.0.0/0"
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-instance-sg" ] ] }
EC2InstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
Roles: [ { "Ref": "EC2InstanceRole" } ]
EC2InstanceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": [ "ec2.amazonaws.com" ] },
"Action": [ "sts:AssumeRole"]
}
]
}
Path: "/"
ManagePolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceForEc2Role"
# Configure RDS
DbInstance:
Type: "AWS::RDS::DBInstance"
Properties:
DBSubnetGroupName: { "Ref": "DbSubnetGroup" }
MultiAZ: "false"
AvailabilityZone: { "Ref": "DBAvailabilityZone" }
AllocatedStorage: 8
StorageType: "gp2"
DBInstanceClass: "db.t2.micro"
DBName: "todobackend"
Engine: "MySQL"
EngineVersion: "5.6"
MasterUserName: { "Ref": "DbUsername" }
MasterUserPassword: { "Ref": "DbPassword" }
VPCSecurityGroups:
- { "Ref": "DbSecurityGroup" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::Stackname" }, "-db" ] ] }
DbSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "Todobackend DB Security Group"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "3306"
ToPort: "3306"
SourceSecurityGroupId: { "Ref": "EC2InstanceSecurityGroup" }
DbSubnetGroup:
Type: "AWS::RDS::DBSubnetGroup"
Properties:
DBSubnetGroupDescription: "Todobackend DB Subnet Group"
SubnetIds: { "Ref": "DbSubnets" }
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-db-subnet-group" ] ] }
# Configure ELB
ElasticLoadBalancer:
Type: "AWS::ElasticLoadBalancing::LoadBalancer"
Properties:
CrossZone: "false"
SecurityGroups: [ { "Ref": "ElbSecurityGroup" } ]
Listeners:
- LoadBalancerPort: "80"
InstancePort: "8000"
Protocol: "http"
HealthCheck:
Target: "HTTP:8000/todos"
HealthyThreshold: "2"
UnhealthyThreshold: "10"
Interval: "30"
Timeout: "5"
Subnets: [ { "Ref": "SubnetId" } ]
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb" ] ] }
ElbSecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: "Todobackend ELB Security Group"
VpcId: { "Ref": "VpcId" }
SecurityGroupIngress:
- IpProtocol: "tcp"
FromPort: "80"
ToPort: "80"
CidrIp: "0.0.0.0/0"
Tags:
- Key: "Name"
Value: { "Fn::Join": ["", [ { "Ref": "AWS::StackName" }, "-elb-sg" ] ] }
# Configure ECS
EcsCluster:
Type: "AWS::ECS::EcsCluster"
TodobackendTaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Name: todobackend
Image: shamdockerhub/todobackend
Memory: 450
Environment:
- Name: DJANGO_SETTINGS_MODULE
Value: todobackend.settings.release
- Name: MYSQL_HOST
Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
- Name: MYSQL_USER
Value: { "Ref": "DbUsername" }
- Name: MYSQL_PASSWORD
Value: { "Ref": "DbPassword" }
MountPoints:
- ContainerPath: /var/www/todobackend
SourceVolume: webroot
Command:
- uwsgi
- "--socket /var/www/todobackend/todobackend.sock"
- "--chmod-socket=666"
- "--module todobackend.wsgi"
- "--master"
- "--die-on-term"
- Name: nginx
Image: shamdockerhub/todobackend-nginx
Memory: 300
PortMappings:
- ContainerPort: "8000"
HostPort: "8000"
MountPoints:
- ContainerPath: /var/www/todobackend
SourceVolume: webroot
Volumes:
- Name: webroot
Host:
SourcePath: /ecs/webroot
TodobackendService:
Type: "AWS::ECS::Service"
Properties:
TaskDefinition: { "Ref": "TodobackendTaskDefinition" }
Cluster: { "Ref": "EcsCluster" }
LoadBalancers:
- ContainerName: "nginx"
ContainerPort: "8000"
LoadBalancerName: { "Ref": "ElasticLoadBalancer" }
Role: { "Ref": "EcsServiceRole" }
DesiredCount: 0
EcsServiceRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [ "ecs.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole"]
}
]
}
Path: "/"
ManagePolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole
TodobackendAdhocTaskDefinition: # Application management task
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Name: todobackend
Image: shamdockerhub/todobackend
Memory: 245
Environment:
- Name: DJANGO_SETTINGS_MODULE
Value: todobackend.settings.release
- Name: MYSQL_HOST
Value: { "Fn::GetAtt": ["DbInstance", "Endpoint.Address"] }
- Name: MYSQL_USER
Value: { "Ref": "DbUsername" }
- Name: MYSQL_PASSWORD
Value: { "Ref": "DbPassword" }
MountPoints:
- ContainerPath: /var/www/todobackend
SourcePath: webroot
Volumes:
- Name: webroot
Host:
SourcePath: /ecs/webroot
# Stack outputs
Outputs:
ElbDomainName:
Description: "Public DNS name of Elastic Load Balancer"
Value: { "Fn::GetAtt": [ "ElasticLoadBalancer", "DNSName" ] }
EcsCluster:
Description: "Amazon resource name (ARN) of Todobackend Ecs Cluster"
Value: { "Ref": "EcsCluster" }
TodobackendTaskDefinition:
Description: "Amazon resource name (ARN) of Todobackend Task definition"
Value: { "Ref": "TodobackendTaskDefinition"}
TodobackendAdhocTaskDefinition:
Description: "Amazon resource name(ARN) of Todobackend Adhoc Task Definition"
Value: { "Ref": "TodobackendAdhocTaskDefinition" }
TodobackendService:
Description: "Amazon resource name (ARN) of Todobackend service"
Value: { "Ref": "TodobackendService" }
below is the error from Outputs block:
Invalid template resource property 'ElbDomainName'
We need dns name of elastic load balancer to be part of Outputs block of stack
Why 'ElbDomainName' an invalid property?
The entire Outputs: block is indented a level too far. Outputs: should not be underneath Resources: indentation-wise
Outputs documentation

Cloudformation unable to create resource policy for apigateway

The resource policy is working fine when i directly pass it to the console.
Below is resource policy example :-
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-west-2:339159142535:ooxmwl6q4e/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
""14.98.8.190/32""
]
}
}
}
]
}
Now how to create a cloudformation template for this to get created and get attached to the apigateway
I tried to create a policy but as per new policy "Principal" is depricated.
I created a role also but no help. Below is role snippet :-
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"Apifirewall": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "Apifirewall",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": [
"arn:aws:execute-api:us-west-2:339159142535:ooxmwl6q4e/*"
],
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"14.98.8.190/32"
]
}
}
}
]
}
}
]
}
}
},
"Outputs": {
"Apifirewall": {
"Value": {
"Fn::GetAtt": [
"Apifirewall",
"Arn"
]
}
}
}
}
APIGateway resource policy is not binding to IAM Policy, it's different kind of resource.
So to implement it on your RestApi your should use the Policy parameter on AWS::ApiGateway::RestApi resource on
{
"Type" : "AWS::ApiGateway::RestApi",
"Properties" : {
"ApiKeySourceType" : String,
"BinaryMediaTypes" : [ String, ... ],
"Body" : JSON object,
"BodyS3Location" : S3Location,
"CloneFrom" : String,
"Description" : String,
"EndpointConfiguration" : EndpointConfiguration,
"FailOnWarnings" : Boolean,
"MinimumCompressionSize" : Integer,
"Name" : String,
"Parameters" : { String:String, ... },
"Policy" : JSON object
}
}
Below is the entire CFT for api deployment with lambda integration
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"AppEnv": {
"Type": "String",
"Description": "Application environment, for this deployment"
},
"DeployTag": {
"Type": "String",
"Description": "Distinct deployment tag ex: BLUE, GREEN"
}
},
"Resources": {
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/AWSLambdaFullAccess"
]
}
},
"RecommenderLambda": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "recommender_field_validation_lambda.lambda_handler",
"FunctionName": "recommenderlambda2",
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Environment": {
"Variables": {
"S3_BUCKET": "belcorp.recommender.test",
"REGION_NAME": "us-west-2",
"TOPIC_ARN": {
"Fn::ImportValue": "RecommenderTopicARN"
},
"TABLE_NAME": {
"Fn::ImportValue": "recommederrequestinfo"
}
}
},
"Code": {
"S3Bucket": "belcorp.recommender.lambdas",
"S3Key": "recommender_field_validation_lambda.zip"
},
"Runtime": "python3.6",
"Timeout": 25
}
},
"LambdaPermission": {
"DependsOn": "RecommenderLambda",
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:invokeFunction",
"FunctionName": "recommenderlambda2",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "RecommenderApi"
},
"/*"
]
]
}
}
},
"RecommenderApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"EndpointConfiguration": {
"Types": [
"EDGE"
]
},
"Description": "RecommenderAPI",
"Name": {
"Fn::Sub": "RecommenderApi-${AppEnv}-${DeployTag}"
},
"Policy": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": {
"Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"
},
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"14.98.8.190/32"
]
}
}
}
]
}
}
},
"ApiGatewayAccount": {
"Type": "AWS::ApiGateway::Account",
"Properties": {
"CloudWatchRoleArn": {
"Fn::ImportValue": "cloudwatchRole"
}
}
},
"ApiDeployment": {
"Type": "AWS::ApiGateway::Deployment",
"DependsOn": [
"OfferPostMethod",
"OrderPostMethod"
],
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"StageName": "dev"
}
},
"ProcessInput": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"ParentId": {
"Fn::GetAtt": [
"RecommenderApi",
"RootResourceId"
]
},
"PathPart": "process-input"
}
},
"OfferLevel": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"ParentId": {
"Ref": "ProcessInput"
},
"PathPart": "offer-level"
}
},
"OrderLevel": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"ParentId": {
"Ref": "ProcessInput"
},
"PathPart": "order-level"
}
},
"OfferPostMethod": {
"DependsOn": "RecommenderLambda",
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"ResourceId": {
"Ref": "OfferLevel"
},
"HttpMethod": "POST",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"RecommenderLambda",
"Arn"
]
},
"/invocations"
]
]
},
"IntegrationResponses": [
{
"StatusCode": 200,
"ResponseTemplates": {
"application/json": "$input.json('$.body')"
}
}
]
}
}
},
"OrderPostMethod": {
"DependsOn": "RecommenderLambda",
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RestApiId": {
"Ref": "RecommenderApi"
},
"ResourceId": {
"Ref": "OrderLevel"
},
"HttpMethod": "POST",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"RecommenderLambda",
"Arn"
]
},
"/invocations"
]
]
},
"IntegrationResponses": [
{
"StatusCode": 200,
"ResponseTemplates": {
"application/json": "$input.json('$.body')"
}
}
]
}
}
}
},
"Outputs": {
"RootUrl": {
"Description": "Root URL of the API gateway",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "RecommenderApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".amazonaws.com"
]
]
}
},
"OfferUrl": {
"Description": "Root URL of the API gateway",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "RecommenderApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".amazonaws.com",
"/dev/process-input/offer-level"
]
]
}
},
"OrderUrl": {
"Description": "Root URL of the API gateway",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "RecommenderApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".amazonaws.com",
"/dev/process-input/order-level"
]
]
}
}
}
}
Too long for a comment. This is the transformed YAML from this answer, which a commenter pointed out can be done in CloudFormation Designer:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
AppEnv:
Type: String
Description: 'Application environment, for this deployment'
DeployTag:
Type: String
Description: 'Distinct deployment tag ex: BLUE, GREEN'
Resources:
LambdaExecutionRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AWSLambdaFullAccess'
RecommenderLambda:
Type: 'AWS::Lambda::Function'
Properties:
Handler: recommender_field_validation_lambda.lambda_handler
FunctionName: recommenderlambda2
Role: !GetAtt
- LambdaExecutionRole
- Arn
Environment:
Variables:
S3_BUCKET: belcorp.recommender.test
REGION_NAME: us-west-2
TOPIC_ARN: !ImportValue RecommenderTopicARN
TABLE_NAME: !ImportValue recommederrequestinfo
Code:
S3Bucket: belcorp.recommender.lambdas
S3Key: recommender_field_validation_lambda.zip
Runtime: python3.6
Timeout: 25
LambdaPermission:
DependsOn: RecommenderLambda
Type: 'AWS::Lambda::Permission'
Properties:
Action: 'lambda:invokeFunction'
FunctionName: recommenderlambda2
Principal: apigateway.amazonaws.com
SourceArn: !Join
- ''
- - 'arn:aws:execute-api:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':'
- !Ref RecommenderApi
- /*
RecommenderApi:
Type: 'AWS::ApiGateway::RestApi'
Properties:
EndpointConfiguration:
Types:
- EDGE
Description: RecommenderAPI
Name: !Sub 'RecommenderApi-${AppEnv}-${DeployTag}'
Policy:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal: '*'
Action: 'execute-api:Invoke'
Resource: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*'
Condition:
IpAddress:
'aws:SourceIp':
- 14.98.8.190/32
ApiGatewayAccount:
Type: 'AWS::ApiGateway::Account'
Properties:
CloudWatchRoleArn: !ImportValue cloudwatchRole
ApiDeployment:
Type: 'AWS::ApiGateway::Deployment'
DependsOn:
- OfferPostMethod
- OrderPostMethod
Properties:
RestApiId: !Ref RecommenderApi
StageName: dev
ProcessInput:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref RecommenderApi
ParentId: !GetAtt
- RecommenderApi
- RootResourceId
PathPart: process-input
OfferLevel:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref RecommenderApi
ParentId: !Ref ProcessInput
PathPart: offer-level
OrderLevel:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref RecommenderApi
ParentId: !Ref ProcessInput
PathPart: order-level
OfferPostMethod:
DependsOn: RecommenderLambda
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref RecommenderApi
ResourceId: !Ref OfferLevel
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- RecommenderLambda
- Arn
- /invocations
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: $input.json('$.body')
OrderPostMethod:
DependsOn: RecommenderLambda
Type: 'AWS::ApiGateway::Method'
Properties:
RestApiId: !Ref RecommenderApi
ResourceId: !Ref OrderLevel
HttpMethod: POST
AuthorizationType: NONE
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- RecommenderLambda
- Arn
- /invocations
IntegrationResponses:
- StatusCode: 200
ResponseTemplates:
application/json: $input.json('$.body')
Outputs:
RootUrl:
Description: Root URL of the API gateway
Value: !Join
- ''
- - 'https://'
- !Ref RecommenderApi
- .execute-api.
- !Ref 'AWS::Region'
- .amazonaws.com
OfferUrl:
Description: Root URL of the API gateway
Value: !Join
- ''
- - 'https://'
- !Ref RecommenderApi
- .execute-api.
- !Ref 'AWS::Region'
- .amazonaws.com
- /dev/process-input/offer-level
OrderUrl:
Description: Root URL of the API gateway
Value: !Join
- ''
- - 'https://'
- !Ref RecommenderApi
- .execute-api.
- !Ref 'AWS::Region'
- .amazonaws.com
- /dev/process-input/order-level
If you are using YAML for CloudFormation, the Policy can be in YAML. There is no need to use JSON for it. For example:
Parameters:
ApiAllowedIps:
Type: CommaDelimitedList
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
...
Policy:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: '*'
Principal: '*'
Resource: '*'
Condition:
IpAddress:
aws:SourceIp: !Ref ApiAllowedIps