AWS change record set to private ip address - amazon-web-services

I'm trying to write a script that would allow the instances to update a record set in AWS each time a new one is spun up.
I'm following this guide:
https://aws.amazon.com/premiumsupport/knowledge-center/simple-resource-record-route53-cli/
My sample.json looks like this:
{
"Comment": "CREATE/DELETE/UPSERT a record ",
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "test.mydomain.com",
"Type": "A",
"TTL": 300,
"ResourceRecords": [{ "Value": "4.4.4.4"}]
}}]
}
I want to replace the 4.4.4.4 but with instance's private IP address.
I tried inserting $IP_ADDRESS there, but obviously, it didn't work.
I also tried entering this manually by doing this:
IP_ADDRESS=$(curl http://169.254.169.254/latest/meta-data/local-ipv4)
aws route53 change-resource-record-sets --hosted-zone-id HKJA837HJS --change-batch {"Comment": "UPSERT a record", "Changes": [{"Action": "UPSERT", "ResourceRecordSet":{"Name":"test.mydomain.com","Type":"A","TTL":300,"ResourceRecords":[{"Value":"$IP_ADDRESS"}]}}]}
When I do this I keep getting the following error:
Unknown options: Changes:, [{Action:, UPSERT,, ResourceRecordSet:Name:test.mydomain.com}]}, ResourceRecordSet:Type:A}]}, ResourceRecordSet:TTL:300}]}, ResourceRecordSet:ResourceRecords:[{Value:}]}]}, UPSERT a record,
I tried re-formatting this numerous times but there's always something wrong.
How can I make sure the instance's IP address is inserted in that recordset each time a new instance in launched?

Trying to pass JSON via the command line is very difficult to get correct because of the need to escape your quote marks.
Instead put your json into a file. Execute a command like this:
sed "s/4.4.4.4/$NEWIP/g" update_rr.json
aws --profile PROD route53 change-resource-record-sets --hosted-zone-id ABCDEFGH012345 --change-batch file://update_rr.json

Related

AWS CLI --output text on a single line of first and second level json

I am trying to get a list of certificates (let's say 100) from AWS Certificates Manager with their Domain Name, Expiry Data, Validation Status and Validation Method with the aws cli command aws acm describe-certificate.
I tried nesting filtering and --ouput text but the output is on two lines. I guess the reason is that ValidationStatus and ValidationMethod are second level in the json ouput after Certificate/DomainValidationOptions.
How would it be possible to get the text ouput in a single line?
Like
foo.bar.com 2022-06-18T23:59:59+00:00 FAILED DNS
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/acm/describe-certificate.html
This is the --output text
$ aws acm describe-certificate --certificate-arn arn:aws:acm:region:aws-account_id:certificate/xxxx --query Certificate.[DomainName,NotAfter,DomainValidationOptions[].[ValidationStatus,ValidationMethod]] --output text
foo.bar.com 2022-06-18T23:59:59+00:00
FAILED DNS
This is the --output json (default output)
aws acm describe-certificate --certificate-arn arn:aws:acm:region:aws-account_id:certificate/xxxx --query Certificate.[DomainName,NotAfter,DomainValidationOptions[].[ValidationStatus,ValidationMethod]]
[
"foo.bar.com",
2022-06-18T23:59:59+00:00,
[
[
"FAILED",
"DNS"
]
]
]
A way to achieve this is to flatten the array you are receiving from the AWS command, with the help of the JMESPath flatten operator — [] — in your query.
Having a query like this:
Certificate.[
DomainName,
NotAfter,
DomainValidationOptions[].[ValidationStatus, ValidationMethod]
][][]
Would give you, with one domain the JSON output
[
"www.example.com",
"2022-06-18T23:59:59+00:00",
"FAILED",
"DNS"
]
And end up on one line.
Now mind that, if you have more than one item in the DomainValidationOptions array, they will, then, all output on the same line, because you'll have one array with everything.
Example for two domains:
[
"www.example.com",
"2022-06-18T23:59:59+00:00",
"FAILED",
"DNS",
"FAILED",
"DNS"
]
See the DNS and ValidationStatus and ValidationMethod repeating for the two domains there?
What you might want to do, is to query the DomainName from the DomainValidationOptions, at least:
Certificate.[
NotAfter,
DomainValidationOptions[].[DomainName, ValidationStatus, ValidationMethod]
][][]
Which would still be on one line, but will get you the domain related to the validation:
[
"2022-06-18T23:59:59+00:00",
"www.example.com",
"FAILED",
"DNS",
"www.example.net",
"FAILED",
"DNS"
]
Some progress was made using John's suggestion.
By using "InUseBy[0]" and not only "InUseBy", I managed to output the "InUseBy" on the first line; otherwise it would be on the second line.
Also using "DomainValidationOptions[0].ValidationStatus" instead of "DomainValidationOptions.ValidationStatus" outputs this on the 2nd line instead of 3rd.
Still, I would like
"RenewalSummary.[RenewalStatus,DomainValidationOptions[0].ValidationStatus]"
to be output on the first line too. I noticed RenewalSummary is an object ,{}, not an array, [].
"RenewalSummary": {
"RenewalStatus": "PENDING_VALIDATION",
"DomainValidationOptions": [
{
"DomainName": "foo.bar.com",
"ValidationDomain": "foo.bar.com",
"ValidationStatus": "PENDING_VALIDATION",
"ResourceRecord": {
"Name": "_9d77eed0XXX66.foo.bar.com.",
"Type": "CNAME",
"Value": "_a5XXXX3.tgztlnjmjp.acm-validations.aws."
},
"ValidationMethod": "DNS"
}
],
"UpdatedAt": "2022-06-24T11:16:34.617000+00:00"
},
aws acm describe-certificate --certificate-arn arn:aws:acm:region:aws-account_id:certificate/xxxx --query Certificate.[DomainName,Issuer,Status,FailureReason,NotAfter,InUseBy[0],RenewalSummary.[RenewalStatus,DomainValidationOptions[0].ValidationStatus]] --output text
This is how the output looks now:
foo.bar.com Amazon ISSUED None 2022-06-18T23:59:59+00:00 arn:aws:elasticloadbalancing:eu-west-1:aws_account_id:loadbalancer/app/alb_foo_bar/XXX
PENDING_VALIDATION PENDING_VALIDATION

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} } } ]}'

AWS/Ansible - Is there a simple way of looking up the ID for a route53 health check using only the name tag?

I'm writing an ansible playbook which interacts with the AWS CLI.
I'd like to be able to look up a route53 health check based on its name tag. For example, I'd like to be able to test whether a route53 health check already exists based on its name tag. Alternatively, I'd like to be able to create an aggregate health check based on other health checks, so I need to be able to look up the ID of the child health checks based on their name tags.
I can look up a list of existing health checks:
aws route53 list-health-checks
However, this does not do what I need, since it returns details of all health checks, but only by ID. It does not return any detail about tags, for example:
UK165342:~ rsaunderson$ aws route53 list-health-checks
{
"HealthChecks": [
{
"HealthCheckConfig": {
"InsufficientDataHealthStatus": "LastKnownStatus",
"Type": "CLOUDWATCH_METRIC",
"AlarmIdentifier": {
"Region": "eu-west-1",
"Name": "my cloudwatch alert name"
},
"Inverted": false
},
"CallerReference": "20363f2b-f482-419d-a5bc-47646d68af90",
"HealthCheckVersion": 1,
"Id": "03ae403b-c33b-4568-8552-7931b2cadc0e"
}
]
}
I can look up the tags for a health check, but only if I already know the ID for the healthcheck. For example:
UK165342:~ rsaunderson$ aws route53 list-tags-for-resource --resource-type healthcheck --resource-id 5bb5e893-c50b-410c-9f84-b9ef3d5ee566
{
"ResourceTagSet": {
"ResourceType": "healthcheck",
"ResourceId": "5bb5e893-c50b-410c-9f84-b9ef3d5ee566",
"Tags": [
{
"Value": "some R53 health check name",
"Key": "Name"
}
]
}
}
I can get the list of all healthcheck IDs, then look up the tags individually for each one, but this seems very inefficient. Is there a better way to do this? Possibly with a custom ansible module?
Unfortunately, you have the best (if inefficient) solution. There's no API in Route53 that allows to filter health checks based on their tags.

How can get ec2 instance ip from instance id in ansible

I am using ansible cloudformation to create stack with 20 instances.
Now in the ansible output i can only see the instance ids.
Now after the stack is created i want to connect to them and configure it but i am not sure how can get thos ips or hostnames from instance id.
cloudformation output is like this
{
"last_updated_time": null,
"logical_resource_id": "test2",
"physical_resource_id": "i-24tf97306",
"resource_type": "AWS::EC2::Instance",
"status": "CREATE_COMPLETE",
"status_reason": null
},
{
"last_updated_time": null,
"logical_resource_id": "test1",
"physical_resource_id": "i-6533184348",
"resource_type": "AWS::EC2::Instance",
"status": "CREATE_COMPLETE",
"status_reason": null
}
ec2_remote_facts module is your friend here.
You can retrieve instance meta data when Ansible runs on the instance e.g.
curl http://169.254.169.254/latest/meta-data/public-hostname
ec2-aa-bb-cc-ddd.ap-southeast-2.compute.amazonaws.com
where aa-bb-cc-ddd represents your IP and the full string represents the hostname.
You're using Ansible so you could use Ansible's get_url module: http://docs.ansible.com/ansible/get_url_module.html to perform the HTTP request.

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.