Adding parameters to api using cloudformation - aws-cloudformation

I tried the cloudformation template that I found here...
https://bl.ocks.org/magnetikonline/c314952045eee8e8375b82bc7ec68e88
It works as expected. But I will like to provide parameters to the post request. My Curl command should look something like this...
curl -d "mynumber=12345" -X POST https://tyin2sswj2.execute-api.us-east-1.amazonaws.com/mycall
How do I handle it at API gateway in the cloudformation template? I have already set the environment variable at lambda function level.
The template that does not work is this...
https://raw.githubusercontent.com/shantanuo/cloudformation/master/updated/lambda_api.tpl.txt
As it is clear that I am not able to pass the "mnumber" variable through the gateway.
I have updated my template and now it deploys function and gateway corretly. And still the URL generated does not work and shows "internal server error" message.
https://raw.githubusercontent.com/shantanuo/cloudformation/master/testapi.tpl.txt

You should change to using HTTP proxy integration.
Here is some info from AWS on proxy integration: https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-http-integrations.html
Try changing your RequestParameters from:
RequestParameters:
method.request.querystring.mnumber: false
to
RequestParameters:
method.request.path.proxy: true
and under integration from:
RequestParameters:
integration.request.querystring.mnumber: "method.request.querystring.mnumber"
to
RequestParameters:
integration.request.path.proxy: 'method.request.path.proxy'
This is a good tutorial on proxy integration with API Gateway:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-http.html

There are two ways you can access mynumber
Method 1 which works best with the SAM serverless API template. Define "API Gateway" and "Lambda". In Lambda definitions, call Events of type API:
This makes it where query strings are automatically picked up due to the event property. The parameters can be found in the event response that is passed into all lambda functions. It can be accessed with multiValueQueryStringParameters or queryStringParameters from the event object.
exports.getByDateHandler = async (event) => {
console.info(event.queryStringParameters);
console.info(event.multiValueQueryStringParameters);
}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Description",
"Transform": ["AWS::Serverless-2016-10-31"],
"Resources": {
"getByDateFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "src/handlers/getByDate/get-by-date.getByIdHandler",
"Runtime": "nodejs14.x",
"Architectures": ["x86_64"],
"MemorySize": 128,
"Timeout": 100,
"Events": {
"Api": {
"Type": "Api",
"Properties": {
"Path": "/date",
"Method": "GET"
}
}
}
}
}
},
"Outputs": {
"WebEndpoint": {
"Description": "API Gateway endpoint URL for Prod stage",
"Value": {
"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
}
}
}
}
Method 2 which I havent tested is by defining the "Lambda", "API Gateway", "API Resource" and "API Methods". Linking the Lambda using the URI statement under "API Method".
for this method I only have a yaml example
MyLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: "Node.js Express REST API"
FunctionName: "get_list_function" (The name in AWS console)
Handler: lambda.handler
Runtime: nodejs12
MemorySize: 128
Role: <ROLE ARN>
Timeout: 60
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "example-api-gw"
Description: "Example API"
ProxyResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
PathPart: '{proxy+}' OR "a simple string like "PetStore"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: NONE
HttpMethod: ANY
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
IntegrationResponses:
- StatusCode: 200
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations
ResourceId: !Ref ProxyResource
RestApiId: !Ref "apiGateway"

Related

Cloudformation to covert string into list on apigateway policy document with condition

Below is the my cloudformation template. I want to convert aws:SourceVpc into list in the resource policy document. I tried with spilt but cft below error.
Invalid policy document. Please check the policy syntax and ensure that Principals are valid. (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: xxxxx; Proxy: null)
Also devl and sdbx have 1 value where as others acnt and acpt have two values in mappings how can do it. Please suggest.
Output should like below.
aws:SourceVpc: ["vpc-7830jkd", "vpc-a1236"]
Mappings:
vpcid:
us-east-1:
sdbx: "vpc-1234"
devl: "vpc-2345"
acpt: "vpc-7830jkd,vpc-a1236"
us-east-2:
acnt: "vpc-a1236,vpc-7830jkd"
Parameters:
Env:
Type: String
Resources:
apigateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: mygateway
EndpointConfiguration:
Types:
- "PRIVATE"
Policy: !Sub
- |-
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
]
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "execute-api:Invoke",
"Resource": [
"execute-api:/*"
],
"Condition": {
"StringNotEquals": {
"aws:SourceVpc": !Split ["," , "${myappid}"]
}
}
}
]
}
- { myappid: !FindInMap [ vpcid, !Ref "AWS::Region", !Ref "Env" ] }
Output should like below. aws:SourceVpc: ["vpc-7830jkd", "vpc-a1236"]
Sadly, you can't do this. CFN is very limited in what it can do, and what you want is simply not achievable with plain CFN. You have to modify your Maps and provide all the vpc ids individually, not mix lists with individual values.
The other way would be through development of CFN macro or custom resource.

Adding lambda integration to HttpApi routes with SAM

I am currently attempting to have a AWS::Serverless::HttpApi integrate with a group of AWS::Serverless::Function's. The goal is to define these resources within a SAM template, and define the actual API using a swagger file.
I have my SAM template defined as so:
Resources:
apiPing:
Type: AWS::Serverless::Function
Properties:
Description: 'Ping'
CodeUri: ../bin/cmd-api-ping.zip
Handler: cmd-api-ping
Runtime: go1.x
Role:
Fn::GetAtt: apiLambdaRole.Arn
Events:
PingEvent:
Type: HttpApi
Properties:
ApiId: !Ref api
Path: /ping
Method: post
api:
Type: AWS::Serverless::HttpApi
Properties:
StageName: prod
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: swagger.yaml
AccessLogSettings:
DestinationArn: !GetAtt accessLogs.Arn
Format: $context.requestId
And my swagger file:
openapi: 3.0.1
info:
title: 'API'
version: 2019-10-13
paths:
/ping:
post:
summary: 'invoke ping'
operationId: 'apiPing'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PingRequest'
required: true
responses:
'200':
description: 'Successful'
content:
application/json:
schema:
$ref: '#/components/schemas/PongResponse'
x-amazon-apigateway-integration:
httpMethod: "POST"
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations
responses:
default:
statusCode: "200"
contentHandling: "CONVERT_TO_TEXT"
passthroughBehavior: "when_no_match"
components:
schemas:
PingRequest:
description: 'a ping request'
type: object
properties:
ping:
description: 'some text'
type: string
PongResponse:
description: 'a pong response'
type: object
properties:
pong:
description: 'some text'
type: string
This template deploys without any errors, however there is no integration attached to the /ping POST route.
The transformed template in CloudFormation does show a loaded swagger file:
"api": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"Body": {
"info": {
"version": 1570924800000,
"title": "API"
},
"paths": {
"/ping": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PingRequest"
}
}
},
"required": true
},
"x-amazon-apigateway-integration": {
"contentHandling": "CONVERT_TO_TEXT",
"responses": {
"default": {
"statusCode": "200"
}
},
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations"
},
"httpMethod": "POST",
"passthroughBehavior": "when_no_match",
"type": "aws_proxy"
},
"summary": "invoke ping",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PongResponse"
}
}
},
"description": "Successful"
}
},
"operationId": "apiPing"
}
}
},
"openapi": "3.0.1",
"components": {
"schemas": {
"PingRequest": {
"type": "object",
"description": "a ping request",
"properties": {
"ping": {
"type": "string",
"description": "some text"
}
}
},
"PongResponse": {
"type": "object",
"description": "a pong response",
"properties": {
"pong": {
"type": "string",
"description": "some text"
}
}
}
}
},
"tags": [
{
"name": "httpapi:createdBy",
"x-amazon-apigateway-tag-value": "SAM"
}
]
}
}
}
I'm trying to understand what I may need to change or add to add the integration to the http api. I can't find any clear explanation in the aws documentation.
I have managed to resolve this. aws::serverless::httpapi creates a AWS::ApiGatewayV2::Api resource. This requires a different integration than the previous versioned ApiGateway.
x-amazon-apigateway-integration has a key defined, payloadFormatVersion. Despite documentation suggesting both 1.0 and 2.0 are supported, it seems 2.0 must be used. As such, my x-amazon-apigateway-integration has become the following (I did clean it up a bit):
x-amazon-apigateway-integration:
payloadFormatVersion: "2.0"
httpMethod: "POST"
type: "aws_proxy"
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations
responses:
default:
statusCode: "200"
connectionType: "INTERNET"
And with this, integration is applied upon deployment.

Create S3 only user

I am trying to create a S3 only user who will by definition have no access to any other resource. The user can upload and download files from S3. I have created a basic template that can be found here...
https://github.com/shantanuo/aws-cloudformation-templates/blob/master/aws/services/IAM/IAM_Users_Groups_and_Policies.yaml
But it is allowing access to cloudformation that is not necessary in my case. I have read the following pages, but do not know how to include them in my template.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
https://docs.aws.amazon.com/AWSCloudForation/latest/UserGuide/aws-properties-iam-group.html
What are the minimum parameters required to create a S3 only user?
Update:
Here is the cloudformation code that I tried and the error that I got:
error message: The following resource(s) failed to create: [CFNRole, CFNUser]. . Rollback requested by user.
The template:
Parameters:
NewUsername:
NoEcho: 'false'
Type: String
Description: New account username
MinLength: '1'
MaxLength: '41'
ConstraintDescription: the username must be between 1 and 41 characters
Password:
NoEcho: 'true'
Type: String
Description: New account password
MinLength: '1'
MaxLength: '41'
ConstraintDescription: the password must be between 1 and 41 characters
Resources:
CFNUser:
Type: AWS::IAM::User
Properties:
LoginProfile:
Password: !Ref 'Password'
UserName : !Ref 'NewUsername'
CFNRole:
Type: AWS::IAM::Role
Properties :
PermissionsBoundary : arn:aws:iam::aws:policy/AmazonS3FullAccess
RoleName : 'myPermissionBoundary'
CFNKeys:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref 'CFNUser'
Outputs:
AccessKey:
Value: !Ref 'CFNKeys'
Description: AWSAccessKeyId of new user
SecretKey:
Value: !GetAtt [CFNKeys, SecretAccessKey]
Description: AWSSecretAccessKey of new user
I have checked this link, but not sure how to include the policy in my current template.
https://aws.amazon.com/blogs/security/easier-way-to-control-access-to-aws-regions-using-iam-policies/
You are explicit giving your new user access to Cloudformation in your template.
You have this section:
CFNUserPolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: CFNUsers
PolicyDocument:
Statement:
- Effect: Allow
Action: ['cloudformation:Describe*', 'cloudformation:List*', 'cloudformation:Get*']
Resource: '*'
Groups: [!Ref 'CFNUserGroup']
CFNAdminPolicies:
Type: AWS::IAM::Policy
Properties:
PolicyName: CFNAdmins
PolicyDocument:
Statement:
- Effect: Allow
Action: cloudformation:*
Resource: '*'
Groups: [!Ref 'CFNAdminGroup']
In which your allow statements specifically provide access to Cloudformation. If you are trying to give access to s3 then why are you giving access to Cloudformation?
If you want a user to have access to s3 only, and going further one specific s3 bucket then your policy would look something like this (note this is in json):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListAllMyBuckets"
],
"Resource": "arn:aws:s3:::*"
},
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::YOUR-BUCKET",
"arn:aws:s3:::YOUR-BUCKET/*"
]
}
]
}
If you want your user to have access to all buckets, then your policy would look more like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1557048549844",
"Action": "s3:*",
"Effect": "Allow",
"Resource": "*"
}
]
}
See this source: https://objectivefs.com/howto/how-to-restrict-s3-bucket-policy-to-only-one-aws-s3-bucket

How to define an ECR Lifecycle Policy with CloudFormation

In order to limit the number of images in a repository, I'd like to define a Lifecycle policy. Since all the stack is defined with CloudFormation, I'd like to define this policy too.
For example, my policy could be "keep only the most recent 8 images, no matter if tagged or not".
The solution was pretty easy, but since I could not find any example or similar questions (ECR is not mainstream, I know), let me post here the easy solution that I found, which simply requires to insert the policy as JSON into the CloudFormation definition:
MyRepository:
Type: AWS::ECR::Repository
Properties:
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [
{
"rulePriority": 1,
"description": "Only keep 8 images",
"selection": {
"tagStatus": "any",
"countType": "imageCountMoreThan",
"countNumber": 8
},
"action": { "type": "expire" }
}]
}
Of course this is very simplistic, but it's the starting point that I was looking for
You can also define a reference to your PolicyText and later on your parameters.json stringify your policy.
It would look like something like this:
template.yml
Parameters:
lifecyclePolicyText:
Description: Lifecycle policy content (JSON), the policy content the pre-fixes for the microservices and the kind of policy (CountMoreThan).
Type: String
repositoryName:
Description: ECR Repository Name to which we will apply the lifecycle policies.
Type: String
registryId:
Description: AWS account identification number (12 digits)
Type: String
Default: xxxxx
Resources:
Repository:
Type: AWS::ECR::Repository
Properties:
LifecyclePolicy:
LifecyclePolicyText: !Ref lifecyclePolicyText
RegistryId: !Ref registryId
RepositoryName: !Ref repositoryName
Outputs:
Arn:
Value: !GetAtt Repository.Arn
parameters.json
[
{
"ParameterKey": "lifecyclePolicyText",
"ParameterValue": "{'rules':[{'rulePriority':1,'description':'Only keep 8 images','selection':{'tagStatus':'any','countType':'imageCountMoreThan','countNumber':8},'action':{'type':'expire'}}]}"
},
{
"ParameterKey": "repositoryName",
"ParameterValue": "xxxx"
}
]
| will allow you to add text inline.
AWSTemplateFormatVersion: "2010-09-09"
Resources:
ECRRepo:
Type: AWS::ECR::Repository
Properties:
RepositoryName: "images"
LifecyclePolicy:
LifecyclePolicyText: |
{
"rules": [
{
"rulePriority": 2,
"description": "Keep only one untagged image, expire all others",
"selection": {
"tagStatus": "untagged",
"countType": "imageCountMoreThan",
"countNumber": 1
},
"action": {
"type": "expire"
}
}
]
}

AWS API Gateway Method Response in CloudFormation

I am trying to set up my API Gateway so it has this simple method response:
And I am using CloudFormation and I keep running into errors. I believe this is pretty simple but I am stuck after spending hours reading docs. Here is my method resource (in YAML):
MyMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
Integration:
Type: AWS
Credentials: !GetAtt MyRole.Arn
IntegrationHttpMethod: "POST"
Uri:
Fn::Join: [ "", [ "arn:aws:apigateway:", Ref: "AWS::Region", ":states:action/SendTaskSuccess" ] ]
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/json: |
{
"output": "\"Approve link was clicked.\"",
"taskToken": "$input.params('taskToken')"
}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates: {"application/json": "$input.json('$.body')"}
RequestParameters:
method.request.querystring.taskToken: false
OperationName: succeed
ResourceId: !Ref MyResource
RestApiId: !Ref MyApi
Do I need a MethodResponse property?
Ok it looks like I just had to add this:
MethodResponses:
- StatusCode: 200
ResponseModels: { "application/json": "Empty" }
ApiPATCH:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: APIGateway
ResourceId: ProxyResourceROOT
HttpMethod: PATCH
AuthorizationType: NONE
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- LambdaFunction
- Arn
- /invocations
IntegrationResponses:
- StatusCode: 200
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
Yes that's right. You need to add the following:
MethodResponses:
StatusCode: 200
ResponseModels:
application/json: 'Empty'