1

I would like to know if there is a way to declare a AWS::Route53::RecordSet in a CloudFormation config that points to the private IP address of the master node on a EMR cluster that is also defined in the same configuration?

The CloudFormation script should be self-explanatory:

  rVPC:
    Type: AWS::EC2::VPC
    # ... 

  rMyEMRCluster:
    Type: AWS::EMR::Cluster
    # ...

  rPrivateHostedZone:
    Type: AWS::Route53::HostedZone
    Properties:
      Name: "example.com"
      VPCs:
        - VPCId: !Ref rVPC
          VPCRegion: ${AWS::Region}

  rMyRecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref rPrivateHostedZone
      Name: !Sub "sub.example.com"
      Region: ${AWS::Region}
      Type: A
      ResourceRecords:
        # TODO: How can I do something like this:
        # - GetAtt rMyEMRCluster.MasterNodePrivateIpAddress

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
James Wierzba
  • 16,176
  • 14
  • 79
  • 120

3 Answers3

3

Not quite.

The only return value available is MasterPublicDNS. This, however, should resolve to the IP address of the master node.

See the Return Values section of AWS::EMR::Cluster - AWS CloudFormation.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
2

if MasterPublicDNS is resolvable from you local networks. Mostly it not. So you have to convert the MasterPublicDNS to IP Address, which you can do like below

#ResourceRecords: # - !GetAtt rMyEMRCluster.MasterPublicDNS # convert ip-10-11-12-13.ec2.internal to 10.11.12.13 ResourceRecords: - !Join - '.' - - !Select - 1 - !Split - "-" - !Select [0, !Split [".ec2.internal", !GetAtt rMyEMRCluster.MasterPublicDNS]] - !Select - 2 - !Split - "-" - !Select [0, !Split [".ec2.internal", !GetAtt rMyEMRCluster.MasterPublicDNS]] - !Select - 3 - !Split - "-" - !Select [0, !Split [".ec2.internal", !GetAtt rMyEMRCluster.MasterPublicDNS]] - !Select - 4 - !Split - "-" - !Select [0, !Split [".ec2.internal", !GetAtt rMyEMRCluster.MasterPublicDNS]]

Anand K
  • 293
  • 3
  • 12
0

You can try using a custom resource for that. It can use emr:ListInstances to get the IP and then you can use that result in your Route 53 resource.

I haven't tried it, but something like the following should work. You may have to add some delay in there if EMR takes a while to create the master node and CloudFormation doesn't already wait for that.

Resources:
  DescribeClusterRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: DescribeCluster
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Action: elasticmapreduce:ListInstances
                Effect: Allow
                Resource: "*"
  GetClusterPrivateIP:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.6
      Handler: index.handler
      Role: !Sub ${DescribeClusterRole.Arn}
      Timeout: 60
      Code:
        ZipFile: |
          import boto3
          import cfnresponse
          import traceback

          def handler(event, context):
            try:
              response = boto3.client('emr').list_instances(
                  ClusterId=event['ResourceProperties']['ClusterId'],
                  InstanceGroupTypes=['MASTER'],
              )

              ip = response['Instances'][0]['PrivateIpAddress']

              cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, ip)
            except:
              traceback.print_last()
              cfnresponse.send(event, context, cfnresponse.FAIL, {}, "ok")
  MasterIp:
    Type: Custom::EmrMasterIp
    Properties:
      ServiceToken: !Sub ${GetClusterPrivateIP.Arn}
      ClusterId: !Ref rMyEMRCluster
  rMyRecordSet:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneId: !Ref rPrivateHostedZone
      Name: !Sub "sub.example.com"
      Region: ${AWS::Region}
      Type: A
      ResourceRecords:
        !Ref MasterIp
kichik
  • 33,220
  • 7
  • 94
  • 114