CloudFormation AWS::CertificateManager::Certificate automated certificate validation - amazon-web-services

According the AWS docs at here and here I should be able to automate a certificate creation and validation using cloudformation. Apparently when you specify a HostedZoneId in the DomainValidationOptions, it is supposed to create the required DNS record to complete the validation (at least that is what it seems from the very vague documentation). My CF template for the cert looks like this:
Resources:
MyAPICert:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: xxxx.dev.mydomain.io
DomainValidationOptions:
- DomainName: mydomain.io
HostedZoneId: /hostedzone/Z03XXXXXXXXXXXX
ValidationMethod: DNS
'mydomain.io' (changed of course) was registered using AWS as registrar as the documents say must be the case for automated validation to work.
This template above is included in a serverless.yml as a resource. However, when I deploy, the stack creation is just stuck waiting for the DNS record - i.e. it does not add the required CNAME entry as I understand it is supposed to do and as such the stack is stuck.
Has anyone gotten this feature to work?
And, yes, I know about the 3rd party custom resources that try to do the same thing, I don't want to use them if CF is supposed to do this natively now.

I hit the same issue. You need to specify the full domain name including the host in the DomainValidationOptions DomainName parameter, and just specify the hosted zone id:
Resources:
MyAPICert:
Type: AWS::CertificateManager::Certificate
Properties:
DomainName: xxxx.dev.mydomain.io
DomainValidationOptions:
- DomainName: xxxx.dev.mydomain.io
HostedZoneId: Z03XXXXXXXXXXXX
ValidationMethod: DNS
In my testing, the Route53 validation record was added about a minute after running the stack, and the domain successfully validated itslef after about 15 minutes.

If this is stuck as in progress for a long time, it could be that you are using a Private Hosted Zone when you need to use the Public one. Probably you don't use a private CA.
That process should take 2-3 minutes, not more than that.

I just deployed the below template to CloudFormation and it successfully created the validation DNS records and authorised the certificate.
If you were to pass the parameters SiteDnsZoneName=mydomain.io. and SiteDnsZoneId=ABCDEFGHIJKLMNOPQRSTU it would create a SAN cert that covers both mydomain.io and *.mydomain.io
{
"Description": "Deploy wildcard SAN cert inc bare domain. (Must deploy cert to us-east-1 for CloudFront)",
"Parameters": {
"SiteDnsZoneName": {
"Type": "String",
"MinLength": 4,
"Description": "DNS Zone",
"Default": "example.com"
},
"SiteDnsZoneId": {
"Type": "String",
"MinLength": 8,
"Description": "DNS Zone Id",
"Default": "ABCDEFGHIJKLMNOPQRSTU"
}
},
"Resources": {
"SiteCertificate": {
"Type": "AWS::CertificateManager::Certificate",
"Properties": {
"DomainName": {
"Fn::Join": [
".",
[
"*",
{
"Ref": "SiteDnsZoneName"
}
]
]
},
"SubjectAlternativeNames": [
{
"Ref": "SiteDnsZoneName"
}
],
"DomainValidationOptions": [
{
"DomainName": {
"Ref": "SiteDnsZoneName"
},
"HostedZoneId": {
"Ref": "SiteDnsZoneId"
}
}
],
"ValidationMethod": "DNS"
}
}
}
}
Note: If you want to use a cert in CloudFront you have to deploy the cert in us-east-1.
Note 2: Route53 needs to be hosting your DNS Zone, but theres no requirement on AWS being the registrar. Your domain can be registered with any provider, so long as you use the AWS name servers provided by Route53 when you add the zone.

Related

Route53, AWS-CLI : Add Alias A name with aws-cli

I have a requirement, where Kubernetes service is generating an ELB for us with DNS name. I am adding that as an Alias for A, which is giving faster DNS propagation as compared to CNAME. I want to automated adding the entry via shell-script, but it's not working out, I get error as mentioned below. Please note, all e
Error log:
An error occurred (InvalidChangeBatch) when calling the ChangeResourceRecordSets operation: [Tried to create an alias that targets dualstack.AXXXXXXXXX-46346364.eu-central-1.elb.amazonaws.com., type A in zone Z0jshgdjhdg, but the alias target name does not lie within the target zone, Tried to create an alias that targets dualstack.asdgdfhgdfh-56767687.eu-central-1.elb.amazonaws.com., type A in zone Z0XXXXX, but that target was not found]
Command used:
aws route53 change-resource-record-sets --hosted-zone-id /hostedzone/Z0XXXXXXX --change-batch '{"Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "test.dev.domain.com", "Type": "A", "AliasTarget":{ "HostedZoneId": "Z0XXXXX","DNSName": "aesdgdfg-46456.eu-central-1.elb.amazonaws.com","EvaluateTargetHealth": false} } } ]}'
What am I doing wrong?
Hosted Zone as asked:
Based on the comments.
The issue was caused by using wrong HostedZoneId in AliasTarget. The correct one is the HostedZoneId associated with your ALB. It can be found in the EC2 console, in Description tab of your ALB under Hosted zone option.
The command should use two HostedZoneIds:
aws route53 change-resource-record-sets \
--hosted-zone-id /hostedzone/<zone-id-from-route53>\
--change-batch \
'{"Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "test.dev.domain.com", "Type": "A", "AliasTarget":{ "HostedZoneId": "<zone-id-of-ALB>","DNSName": "aesdgdfg-46456.eu-central-1.elb.amazonaws.com","EvaluateTargetHealth": false} } } ]}'

How to create an AWS SFTP server with internet-facing VPC endpoint with Cloudformation?

I am able to create an SFTP Server (AWS Transfer Family) inside a VPC with an internet-facing Endpoint on AWS console as described here: https://docs.aws.amazon.com/transfer/latest/userguide/create-server-in-vpc.html
VPC endpoint type access selection
Now, I need to replicate that very same creation in a CloudFormation template and don't know how to do it (if possible). According to what I see in https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-transfer-server-endpointdetails.html and in the corresponding CDK docs https://docs.aws.amazon.com/cdk/api/latest/docs/#aws-cdk_aws-transfer.CfnServer.EndpointDetailsProperty.html, there seems not to be a was to set the "access" property value.
All the examples I've come across use a PUBLIC endpoint (in contrast to a VPC one). Here's the snipped I'm working on:
"Resources": {
"ftpserver": {
"Type": "AWS::Transfer::Server",
"DependsOn": "sftpEIP1",
"Properties": {
"EndpointDetails": {
"SubnetIds": [
{
"Ref": "sftpSubnet1"
}
],
"VpcId": {
"Ref": "sftpVPC"
}
},
"EndpointType": "VPC",
"Protocols": [
"SFTP"
],
"Tags": [
{
"Key": "KeyName",
"Value": "ValueName"
}
]
}
}
},
...
}
Since there is no way to set the access type in CloudFormation, the endpoint ends up created as "Internal" instead of "Internet-facing" which is what I need.
Is there any way around this or should I just change it manually (AWS console) after every deployment?
You need to associate Elastic IPs and define the security group.
Notice because the Elastic IPs can only be added after the server is created, it takes sometime to complete, CloudFormation actually creates the server with internal only, stops the server, adds the Elastic IPs, it starts again with elastic IPs and internet facing and then stack is completed.
Example with the CF template below works as expected.
Description: Test CF with FTP server
Resources:
ElasticIP1:
Type: AWS::EC2::EIP
ElasticIP2:
Type: AWS::EC2::EIP
ElasticIP3:
Type: AWS::EC2::EIP
FTPServer:
Type: AWS::Transfer::Server
Properties:
EndpointDetails:
AddressAllocationIds:
- !GetAtt ElasticIP1.AllocationId
- !GetAtt ElasticIP2.AllocationId
- !GetAtt ElasticIP3.AllocationId
SecurityGroupIds:
- sg-0c4184c3f5da91d4a
SubnetIds:
- subnet-0546e2c78cebd0a60
- subnet-0114560b841c91de7
- subnet-0af8fb5fae5472862
VpcId: vpc-07daf77a355f5a8e8
EndpointType: VPC
Protocols:
- SFTP

aws cloudformation WAF geo location condition

Trying to create a cloud formation template to configure WAF with geo location condition. Couldnt find the right template yet. Any pointers would be appreciated.
http://docs.aws.amazon.com/waf/latest/developerguide/web-acl-geo-conditions.html
Unfortunately, the actual answer (as of this writing, July 2018) is that you cannot create geo match sets directly in CloudFormation. You can create them via the CLI or SDK, then reference them in the DataId field of a WAFRule's Predicates property.
Creating a GeoMatchSet with one constraint via CLI:
aws waf-regional get-change-token
aws waf-regional create-geo-match-set --name my-geo-set --change-token <token>
aws waf-regional get-change-token
aws waf-regional update-geo-match-set --change-token <new_token> --geo-match-set-id <id> --updates '[ { "Action": "INSERT", "GeoMatchConstraint": { "Type": "Country", "Value": "US" } } ]'
Now reference that GeoMatchSet id in the CloudFormation:
"WebAclGeoRule": {
"Type": "AWS::WAFRegional::Rule",
"Properties": {
...
"Predicates": [
{
"DataId": "00000000-1111-2222-3333-123412341234" // id from create-geo-match-set
"Negated": false,
"Type": "GeoMatch"
}
]
}
}
There is no documentation for it, but it is possible to create the Geo Match in serverless/cloudformation.
Used the following in serverless:
Resources:
Geos:
Type: "AWS::WAFRegional::GeoMatchSet"
Properties:
Name: geo
GeoMatchConstraints:
- Type: "Country"
Value: "IE"
Which translated to the following in cloudformation:
"Geos": {
"Type": "AWS::WAFRegional::GeoMatchSet",
"Properties": {
"Name": "geo",
"GeoMatchConstraints": [
{
"Type": "Country",
"Value": "IE"
}
]
}
}
That can then be referenced when creating a rule:
(serverless) :
Resources:
MyRule:
Type: "AWS::WAFRegional::Rule"
Properties:
Name: waf
Predicates:
- DataId:
Ref: "Geos"
Negated: false
Type: "GeoMatch"
(cloudformation) :
"MyRule": {
"Type": "AWS::WAFRegional::Rule",
"Properties": {
"Name": "waf",
"Predicates": [
{
"DataId": {
"Ref": "Geos"
},
"Negated": false,
"Type": "GeoMatch"
}
]
}
}
I'm afraid that your question is too vague to solicit a helpful response. The CloudFormation User Guide (pdf) defines many different WAF / CloudFront / R53 resources that will perform various forms of geo match / geo blocking capabilities. The link you provide seems a subset of Web Access Control Lists (Web ACL) - see AWS::WAF::WebACL on page 2540.
I suggest you have a look and if you are still stuck, actually describe what it is you are trying to achieve.
Note that the term you used: "geo location condition" doesn't directly relate to an AWS capability that I'm aware of.
Finally, if you are referring to https://aws.amazon.com/about-aws/whats-new/2017/10/aws-waf-now-supports-geographic-match/, then the latest Cloudformation User Guide doesn't seem to have been updated yet to reflect this.

Invalid domain name identifier specified

When trying to create an AWS::ApiGateway::BasePathMapping through CloudFormation, I am given the following error:
Invalid domain name identifier specified
Below is the portion(s) of my CloudFormation template that should create the AWS::ApiGateway::BasePathMapping:
{
"Parameters": {
"ApiDomainName": {
"Description": "The domain name for the API",
"Type": "String"
}
},
"Resources": {
"ApiBasePathMapping": {
"Type": "AWS::ApiGateway::BasePathMapping",
"Properties": {
"DomainName": {
"Ref": "ApiDomainName"
},
"RestApiId": {
"Ref": "RepositoryApi"
},
"Stage": {
"Ref": "ApiProductionStage"
}
},
"DependsOn": [
"ApiProductionStage"
]
}
}
}
The documentation makes no mention that it needs to be anything special for the DomainName, but the documentation for this resource seems to be lacking some information (It doesn't list outputs for example even though there is a Distribution Domain Name created as an example).
The remainder of the stack works as expected. I am trying to add this resource in as a Change Set. I do own the domain I am trying to use, and I have created a certificate in ACM for this domain.
Quoting from AWS forums:
You can only create or modify base path mappings after the domain name
has been added to API Gateway. This "Invalid domain name identifier
specified" error message is returned when the domain name given in the
base path mapping is not found, indicating that it has not been added
yet.
Also, as of March 2017, the only way to add domain name to the API Gateway via CloudFormation is via custom resources that CloudFormation offers.
Ref: https://forums.aws.amazon.com/message.jspa?messageID=769627
It is now possible to just do that. You just have to explicit state on your CFN template that there is a dependency (DependsOn):
...
ApiDevMapping:
Type: 'AWS::ApiGateway::BasePathMapping'
Properties:
BasePath: v1.0
Stage: dev
DomainName: my-api.example.com
RestApiId: !Ref MobileApiDev
DependsOn:
- MobileApiDevDomain
...

How to publish kubernetes LoadBalancer Ingress URL to aws route53

Today when I launch an app using kubernetes over aws it exposes a publicly visible LoadBalancer Ingress URL, however to link that to my domain to make the app accessible to the public, I need to manually go into the aws route53 console in a browser on every launch. Can I update the aws route53 Resource Type A to match the latest Kubernetes LoadBalancer Ingress URL from the command line ?
Kubernetes over gcloud shares this challenge of having to either predefine a Static IP which is used in launch config or manually do a browser based domain linkage post launch. On aws I was hoping I could use something similar to this from the command line
aws route53domains update-domain-nameservers ???
__ OR __ can I predefine an aws kubernetes LoadBalancer Ingress similar to doing a predefined Static IP when over gcloud ?
to show the deployed app's LoadBalancer Ingress URL issue
kubectl describe svc
... output
Name: aaa-deployment-407
Namespace: ruptureofthemundaneplane
Labels: app=bbb
pod-template-hash=4076262206
Selector: app=bbb,pod-template-hash=4076262206
Type: LoadBalancer
IP: 10.0.51.82
LoadBalancer Ingress: a244bodhisattva79c17cf7-61619.us-east-1.elb.amazonaws.com
Port: port-1 80/TCP
NodePort: port-1 32547/TCP
Endpoints: 10.201.0.3:80
Port: port-2 443/TCP
NodePort: port-2 31248/TCP
Endpoints: 10.201.0.3:443
Session Affinity: None
No events.
UPDATE:
Getting error trying new command line technique (hat tip to #error2007s comment) ... issue this
aws route53 list-hosted-zones
... outputs
{
"HostedZones": [
{
"ResourceRecordSetCount": 6,
"CallerReference": "2D58A764-1FAC-DEB4-8AC7-AD37E74B94E6",
"Config": {
"PrivateZone": false
},
"Id": "/hostedzone/Z3II3949ZDMDXV",
"Name": "chainsawhaircut.com."
}
]
}
Important bit used below : hostedzone Z3II3949ZDMDXV
now I craft following using this Doc (and this Doc as well) as file /change-resource-record-sets.json (NOTE I can successfully change Type A using a similar cli call ... however I need to change Type A with an Alias Target of LoadBalancer Ingress URL)
{
"Comment": "Update record to reflect new IP address of fresh deploy",
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "chainsawhaircut.com.",
"Type": "A",
"TTL": 60,
"AliasTarget": {
"HostedZoneId": "Z3II3949ZDMDXV",
"DNSName": "a244bodhisattva79c17cf7-61619.us-east-1.elb.amazonaws.com",
"EvaluateTargetHealth": false
}
}
}]
}
on command line I then issue
aws route53 change-resource-record-sets --hosted-zone-id Z3II3949ZDMDXV --change-batch file:///change-resource-record-sets.json
which give this error message
An error occurred (InvalidInput) when calling the ChangeResourceRecordSets operation: Invalid request
Any insights ?
Here is the logic needed to update aws route53 Resource Record Type A with value from freshly minted kubernetes LoadBalancer Ingress URL
step 1 - identify your hostedzone Id by issuing
aws route53 list-hosted-zones
... from output here is clip for my domain
"Id": "/hostedzone/Z3II3949ZDMDXV",
... importantly never populate json with hostedzone Z3II3949ZDMDXV its only used as a cli parm ... there is a second similarly named token HostedZoneId which is entirely different
step 2 - see current value of your route53 domain record ... issue :
aws route53 list-resource-record-sets --hosted-zone-id Z3II3949ZDMDXV --query "ResourceRecordSets[?Name == 'scottstensland.com.']"
... output
[
{
"AliasTarget": {
"HostedZoneId": "Z35SXDOTRQ7X7K",
"EvaluateTargetHealth": false,
"DNSName": "dualstack.asomepriorvalue39e7db-1867261689.us-east-1.elb.amazonaws.com."
},
"Type": "A",
"Name": "scottstensland.com."
},
{
"ResourceRecords": [
{
"Value": "ns-1238.awsdns-26.org."
},
{
"Value": "ns-201.awsdns-25.com."
},
{
"Value": "ns-969.awsdns-57.net."
},
{
"Value": "ns-1823.awsdns-35.co.uk."
}
],
"Type": "NS",
"Name": "scottstensland.com.",
"TTL": 172800
},
{
"ResourceRecords": [
{
"Value": "ns-1238.awsdns-26.org. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400"
}
],
"Type": "SOA",
"Name": "scottstensland.com.",
"TTL": 900
}
]
... in above notice value of
"HostedZoneId": "Z35SXDOTRQ7X7K",
which is the second similarly name token Do NOT use wrong Hosted Zone ID
step 3 - put below into your change file aws_route53_type_A.json (for syntax Doc see link mentioned in comment above)
{
"Comment": "Update record to reflect new DNSName of fresh deploy",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"AliasTarget": {
"HostedZoneId": "Z35SXDOTRQ7X7K",
"EvaluateTargetHealth": false,
"DNSName": "dualstack.a0b82c81f47d011e6b98a0a28439e7db-1867261689.us-east-1.elb.amazonaws.com."
},
"Type": "A",
"Name": "scottstensland.com."
}
}
]
}
To identify value for above field "DNSName" ... after the kubernetes app deploy on aws it responds with a LoadBalancer Ingress as shown in output of cli command :
kubectl describe svc --namespace=ruptureofthemundaneplane
... as in
LoadBalancer Ingress: a0b82c81f47d011e6b98a0a28439e7db-1867261689.us-east-1.elb.amazonaws.com
... even though my goal is to execute a command line call I can do this manually by getting into the aws console browser ... pull up my domain on route53 ...
... In this browser picklist editable text box (circled in green) I noticed the URL gets magically prepended with : dualstack. Previously I was missing that magic string ... so json key "DNSName" wants this
dualstack.a0b82c81f47d011e6b98a0a28439e7db-1867261689.us-east-1.elb.amazonaws.com.
finally execute the change request
aws route53 change-resource-record-sets --hosted-zone-id Z3II3949ZDMDXV --change-batch file://./aws_route53_type_A.json
... output
{
"ChangeInfo": {
"Status": "PENDING",
"Comment": "Update record to reflect new DNSName of fresh deploy",
"SubmittedAt": "2016-07-13T14:53:02.789Z",
"Id": "/change/CFUX5R9XKGE1C"
}
}
.... now to confirm change is live run this to show record
aws route53 list-resource-record-sets --hosted-zone-id Z3II3949ZDMDXV
You can also use external-dns project.
AWS specific setup can be found here
After installation it can be used with an annotation e.g.: external-dns.alpha.kubernetes.io/hostname: nginx.external-dns-test.my-org.com.
Note the IAM permissions needs to be set properly.