Search code examples
aws-cdkamazon-route53

How to add PTR record for EC2 Instance's Private IP in CDK?


I've two private hosted zones created for populating A records and PTR records corresponding to my EC2 instance's private ip. Yes, it's the private ip that I need. This subnet is routed to our corporate data center, so we need non-cryptic hostnames and consistent reverse lookup on them within the account.

I've got the forward lookup working well, however I'm confused how exactly it should be for the reverse lookup on the IP. Assume, my CIDR is 192.168.10.0/24 where the EC2 instances will get created.

    const fwdZone = new aws_route53.PrivateHostedZone(
      this, "myFwdZone", {
        zoneName: "example.com",
        vpc: myVpc,
    });

    const revZone = new aws_route53.PrivateHostedZone(
      this, "myRevZone", {
        zoneName: "10.168.192.in-addr.arpa",
        vpc: myVpc,
      }
    );

I'm later creating the A record by referencing the EC2 instance's privateIp property. This worked well.

    const myEc2 = new aws_ec2.Instance(this, 'myEC2', {...})

    new aws_route53.RecordSet(this, "fwdRecord", {
      zone: fwdZone,
      recordName: "myec2.example.com",
      recordType: aws_route53.RecordType.A,
      target: aws_route53.RecordTarget.fromIpAddresses(
        myEc2.instancePrivateIp
      ),
    });

However, when I try to create the PTR record for the same, I've got some trouble. I needed to extract the fourth octet and specify as the recordName

    new aws_route53.RecordSet(this, "revRecord", {
      zone: revZone,
      recordName: myEc2.instancePrivateIp.split('.')[3],
      recordType: aws_route53.RecordType.PTR,
      target: aws_route53.RecordTarget.fromValues("myec2.example.com"),
    });

The CDK synthesized CloudFormation template looks odd as well, especially the token syntax.

  revRecordDEADBEEF:
    Type: AWS::Route53::RecordSet
    Properties:
      Name: ${Token[TOKEN.10.168.192.in-addr.arpa.
      Type: PTR
      HostedZoneId: A12345678B00CDEFGHIJ3
      ResourceRecords:
        - myec2.example.com
      TTL: "1800"

Is this the right way to achieve this ? If I specified the recordName as just the privateIp, then the synthesized template ends up doing something else, which I see is incorrect too.

  revRecordDEADBEEF:
    Type: AWS::Route53::RecordSet
    Properties:
      Name:
        Fn::Join:
          - ""
          - - Fn::GetAtt:
                - myEC2123A01BC
                - PrivateIp
            - .10.168.192.in-addr.arpa.
      Type: PTR
      HostedZoneId: A12345678B00CDEFGHIJ3
      ResourceRecords:
        - myec2.example.com
      TTL: "1800"

Solution

  • Answering the CDK part of your question: the original error was because you were performing string manipulation on an unresolved token. Your CDK code runs before any resources are provisioned. This has to be the case, since it generates the CloudFormation template that will be submitted to CloudFormation to provision the resources. So when the code runs, the instance does not exist, and its IP address is not knowable.

    CDK still allows you to access unresolved properties, returning a Token instead. You can pass this token around and it will be resolved to the actual value during deployment.

    To perform string manipulation on a token, you can use CloudFormation's bult-in functions, since they run during deployment, after the token has been resolved.

    Here's what it would look like:

    recordName: Fn.select(0, Fn.split('.', myEc2.instancePrivateIp))
    

    As you found out yourself, you were also selecting the wrong octet of the IP address, so the actual solution would include replacing 0 with 3 in the call.

    References:

    https://docs.aws.amazon.com/cdk/v2/guide/tokens.html

    https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html#intrinsic-functions-and-condition-expressions