AWS Cloudformation: How to get a RestAPI name - amazon-web-services

Goal
Get the name for an API GW created within Cloudformation stack. The API Name is required if trying to get an API GW metric, since they exist By Api Name
.yaml Example
TESTAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Description: 'test api'
EndpointConfiguration:
Types:
- REGIONAL
Problem
If I call !Ref TESTAPI, I get the API ID, and if I call !GetAtt TESTAPI.Name the error is: "invalid resource attribute"
The docs don't show any return values.

Unless the CloudFormation RestApi resource is modified, it will not provide the Name attribute.
Workaround
Create a string Parameter to hold the string and enforce the API Name, which you can refer later with !GetAtt APIName.Value
YAML example
APIName:
Type: AWS::SSM::Parameter
Properties:
AllowedPattern: "^[-a-zA-Z0-9 ]+$"
Type: String
Value: "Some API name"

Related

Is there a way to fetch HostedZoneId from DNS name in CFT?

In CloudFormation Template, can we fetch HostedZoneId from DNS name? One way is we check the resource and look for the HostedZoneId and insert it. Can we also use GetAtt or any other function to get the id.
I want to do something like this on HostedZoneId but this is incorrect. Is there any other way to do this?
Parameter:
VPCEDNS:
Description: VPCe DNS target
Type: String
Resources:
PrimaryRecord:
Type: AWS::Route53::RecordSet
Properties:
AliasTarget:
HostedZoneId: !GetAtt (!Ref "VPCEDNS").HostedZoneId
DNSName: !Ref "VPCEDNS"
EvaluateTargetHealth: true
Failover: PRIMARY
HostedZoneName: example.com
Name: service.example.com
Type: A
Sadly, in plain CFN there is no such way. If you really would need such functionality, you would have to use custom resource.
The resource would be in the form of a lambda function, which would take your DNS name as input parameter. Then using AWS SDK (e.g. boto3), it would find and match the corresponding hosted zone, and return its ID into your CFN stack for further use.

AWS ApiGateway: Unable to retrieve DistributionHostedZoneId attribute for AWS::ApiGateway::DomainName

We are using AWS API Gateway, and I'm using CloudFormation to register a domain and an A record, as follows:
Domain:
Type: AWS::ApiGateway::DomainName
Properties:
# EnvironmentName starts with an upper case letter, but the domain is created anyway
DomainName: !Sub "${EnvironmentName}.mycompany.com"
# this is a single certificate, used for all envs, and must be in virginia
CertificateArn: !ImportValue DomainCertificateArn
SecurityPolicy: TLS_1_2
EndpointConfiguration:
Types:
- EDGE
ARecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: Fn::ImportValue !Sub "${EnvironmentName}-HostedZoneId"
# resolves to Z032215012YHJZINBEW
Type: A
Name: !Ref Domain
AliasTarget:
DNSName: !GetAtt Domain.DistributionDomainName
HostedZoneId: !GetAtt Domain.DistributionHostedZoneId
However, I'm seeing the following error when running this against my environment:
Unable to retrieve DistributionHostedZoneId attribute for AWS::ApiGateway::DomainName
I've looked in the API Gateway console to check on this domain entry, and it does exist, with type EDGE, and all the other params present. The Hosted zone id however is not the same value as that used in the template.
According to the documentation you should be able to get this value from the distributionHostedZoneId attribute of your AWS::ApiGateway::DomainName resource.
Based on the documentation and your file nothing appears to be configured incorrectly, there is also this line in the documentation regarding the DistributionHostedZoneId.
The only valid value is Z2FDTNDATAQYW2 for all regions.
This is the CloudFront distribution ID (as you're using regional) so you could set this value specifically. Otherwise validate that it gets created as a EDGE custom domain.

AWS SAM - Enforcing Request Validation in API Gateway Method by SAM Template

I am working on a SAM application having a lambda function with API Gateway as source of event. API Endpoint is a POST Method requiring a set of parameters in request body. API Gateway provides us the capability of validating request body by specifying a request Model using AWS Console.
Refer Screenshots below of AWS Console options:
I need to set similar options via SAM template and able to link a Model with the request body but not able to set request validator option and is not able to find any documentation or example also.
Below is my SAM Template
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template
Parameters:
Stage:
Type: String
Default: dev
Resources:
MyApiGateway:
Type: AWS::Serverless::Api
Properties:
Name: My AWS Serverless API
StageName: !Ref Stage
Models:
ExchangeRate:
$schema: "http://json-schema.org/draft-04/schema#"
properties:
base:
type: string
target:
type: string
required:
- base
- target
title: User
type: object
ExchangeRateFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: functions/exchange-rate/
Handler: index.handler
Runtime: nodejs12.x
Description: Function to Get Currency Exchange Rate
MemorySize: 128
Timeout: 3
Policies:
- AWSLambdaBasicExecutionRole
Events:
HelloWorld:
Type: Api
Properties:
RestApiId: !Ref MyApiGateway
Path: /exchange
Method: POST
RequestModel:
Model: ExchangeRate
Required: true
Outputs:
ExchangeRateFunction:
Description: "Exchange Rate Lambda Function ARN"
Value: !GetAtt ExchangeRateFunction.Arn
MyApiGateway:
Description: "My Seed API EndPoint"
Value: !Sub "https://${MyApiGateway}.execute-api.${AWS::Region}.amazonaws.com/${Stage}"
Documentation referred
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-api.html
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html
Please let me know how can I set 'Request Validator' to 'Validate body' option using SAM template. Will appreciate the help
Add ValidateBody: true i.e.
RequestModel:
Model: ExchangeRate
Required: true
ValidateBody: true
I've ran into the same problem, apparently this feature is lacking from SAM for a while, as you can see from this previous question:
How to add a request validator in a AWS SAM template for AWS::Serverless::Api?
Also, a few issues have been opened in GitHub, the last one being:
https://github.com/awslabs/serverless-application-model/issues/1403
I've hacked a solution that includes two additional properties in the SAM specification to solve this issue, but I wouldn't expect it to actually become a PR. I can provide further instructions if you'd like to use my forked repo to deploy from a develop branch.

AWS Cloudformation Get Hosted Zone Name from Hosted Zone ID

When taking a parameter of type AWS::Route53::HostedZone::Id is there a way to get the HostedZone name?
The hosted zone already exists but was not created with Cloudformation so there is no way for me to reference the name from another template.
Using type AWS::Route53::HostedZone::Id allows the user to select from a drop down, but the ID is chosen not the name.
Is there a way to get the name from the ID so that a record set can be created?
Here is the template I am using, notice the Name of the record set entry where we need the name of the hosted zone to create the record set.
AWSTemplateFormatVersion: '2010-09-09'
Description: Route53
Parameters:
HostedZone:
Type: AWS::Route53::HostedZone::Id
Description: The Hosted Zone for the Record Set
RecordSetName:
Type: String
Description: The name of the record set (all lowercase)
Resources:
Route53:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId: !Ref HostedZone
Comment: DNS name
Name: !Sub ${RecordSetName}.??????
Type: A
TTL: '60'
ResourceRecords:
- 10.1.1.1
Given the problem you appear to be trying to solve (add an A record for your apex domain) you don't actually need the drop down parameter selector of type AWS::Route53::HostedZone::Id. Instead you can just use your String input and use HostedZoneName instead of HostedZoneId in the AWS::Route53::RecordSet as shown below:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
DomainName:
Type: String
Description: apex domain name
Resources:
Route53:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub '${DomainName}.'
Comment: DNS name
Name: !Ref DomainName
Type: A
TTL: '60'
ResourceRecords:
- 10.1.1.1
(note that you need to add the extra period . onto the end of the DomainName for the HostedZoneName).
If you wanted a sub-domain you could do something like:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
DomainName:
Type: String
Description: apex domain name
DomainPrefix:
Type: String
Description: sub domain prefix
Resources:
Route53:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub '${DomainName}.'
Comment: DNS name
Name: !Sub '${DomainPrefix}.${DomainName}'
Type: A
TTL: '60'
ResourceRecords:
- 10.1.1.2
With reference to Fn::GetAtt, you would use these when creating cloudformation exports for your resources, not when using the resources as in this question.
You can if you wish create exports containing the apex domain name and hosted zone ids, which is what I prefer to do to keep things tidy. However, exports are region specific, so if you deploy across multiple regions (which might be forced on you if you are using CloudFront and wants APIs deployed to other than us-east-1) you will need some faking up the exports in some of the regions.
Hosted Zone ID is displayed in Route 53 console UI and looks like Z1AVC899B05E2Y
Fn::GetAtt
The Fn::GetAtt intrinsic function returns a value for a specified attribute of this type. The following are the available attributes and sample return values.
For more information about using the Fn::GetAtt intrinsic function, see Fn::GetAtt.
NameServers
Returns the set of name servers for the specific hosted zone. For example: ns1.example.com.
This attribute is not supported for private hosted zones.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-hostedzone.html

Passing AWS region and account ID to swagger

I am defining my API Gateway APIs using AWS SAM
ApiGatewayApi:
Type: AWS::Serverless::Api
Properties:
DefinitionUri: swagger.yml
StageName: prod
Variables:
Region: !Ref AWS::Region
AccountId: !Ref AWS::AccountId
Ec2Index: !Ref Ec2Index
AuthLogin: !Ref AuthLogin
Ec2Patch: !Ref Ec2Patch
AutoScalingIndex: !Ref AutoScalingIndex
AutoScalingPatch: !Ref AutoScalingPatch
AutoScalingScale: !Ref AutoScalingScale
In my swagger file:
paths:
/auth/session:
post:
produces:
- application/json
x-amazon-apigateway-integration:
uri: arn:aws:apigateway:ap-southeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-1:598545985414:function:${stageVariables.AuthLogin}/invocations
passthroughBehavior: when_no_match
httpMethod: POST
type: aws_proxy
responses:
200:
description: App token
401:
description: 401
403:
description: 403
AWS CloudFormation errors saying
Errors found during import: Unable to put integration on 'POST' for resource at path '/auth/session': Lambda function ARN must be in same account Unable to put integration on 'GET' for resource at path '/autoscaling': Lambda function ARN must be in same account Unable to put integration on 'PATCH' for resource at path '/autoscaling/{groupName}': Lambda function ARN must be in same account Unable to put integration on 'POST' for resource at path '/autoscaling/{groupName}/scale': Lambda function ARN must be in same account Unable to put integration on 'GET' for resource at path '/ec2': Lambda function ARN must be in same account Unable to put integration on 'PATCH' for resource at path '/ec2/{id}': Lambda function ARN must be in same account
Seems like my ARN is invalid. This is resolved once I remove the variables. Whats wrong here?
As of now swagger does not allow AccountId in stage variables. It's a limitation of the API Gateway.
You can get around this by just using a stage variable for the function name only and piece the rest together like this:
//does not get passed in. This is just a placeholder for the stage variable
Parameters:
ApiFunctionName:
Type: String
Description: Function name of the api lambda function
Default: ${stageVariables.yourFunctionNameVar}
//in your gateway path
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
!Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${ApiFunctionName}/invocations"
Passing variables to swagger is not possible to do at this moment from a SAM template, but you can copy entire swagger file in the template file as DefinitionBody and reference variables the same way provided swagger definition is not huge.
SAM template is currently limited to 51.2 KB