Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Route53: ElasticBeanstalkEnvironmentEndpointTarget throws exception for single instance environment #31843

Open
1 task
ivanovvitaly opened this issue Oct 22, 2024 · 3 comments
Assignees
Labels
@aws-cdk/aws-route53 Related to Amazon Route 53 bug This issue is a bug. effort/medium Medium work item – several days of effort p1

Comments

@ivanovvitaly
Copy link

ivanovvitaly commented Oct 22, 2024

Describe the bug

I'm deploying elastic beanstalk single instance application and getting error when creating an A record alias to the EB environment.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Version

No response

Expected Behavior

Exception is not thrown during sync/deploy and the alias A record is created

Current Behavior

Unhandled exception. System.Exception: Cannot use an EBS alias as `environmentEndpoint`. You must find your EBS environment endpoint via the AWS console. See the Elastic Beanstalk developer guide: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customdomains.html
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
   at Amazon.JSII.Runtime.Services.Client.Create(CreateRequest request)
   at Amazon.JSII.Runtime.Services.Client.Create(String fullyQualifiedName, Object[] arguments, Override[] overrides, String[] interfaces)
   at Amazon.JSII.Runtime.Deputy.DeputyBase..ctor(DeputyProps props)
   at Constructs.Construct..ctor(DeputyProps props)
   at Amazon.CDK.Resource..ctor(DeputyProps props)
   at Amazon.CDK.AWS.Route53.RecordSet..ctor(DeputyProps props)
   at Amazon.CDK.AWS.Route53.ARecord..ctor(Construct scope, String id, IARecordProps props)

Reproduction Steps

CfnEnvironment env = ...
var hostedZone = HostedZone.FromLookup(this, "HostedZone", new HostedZoneProviderProps
{
    DomainName = props.HostedZoneName
});
var aRecord = new ARecord(this, "AliasRecord", new ARecordProps
{
    RecordName = props.DnsName,
    Zone = hostedZone,
    Target = RecordTarget.FromAlias(new ElasticBeanstalkEnvironmentEndpointTarget(env.AttrEndpointUrl))
});

Possible Solution

No response

Additional Information/Context

I know that AttrEndpointUrl returns IP address for the single instance deployment. I tried to resolve the environment url using a custom resource, but that didn't help either, though environmentUrl is accurate in response.

CfnEnvironment apiEnvironment;

var getEnvironmentUrl = new AwsCustomResource(this, "GetEnvironmentUrl", new AwsCustomResourceProps
{
    OnUpdate = new AwsSdkCall
    {
        Service = "elasticbeanstalk",
        Action = "DescribeEnvironments",
        Parameters = new Dictionary<string, object>
        {
            ["EnvironmentNames"] = new[] { apiEnvironment.EnvironmentName }
        },
        PhysicalResourceId = PhysicalResourceId.Of(DateTime.Now.ToString(CultureInfo.InvariantCulture))
    },
    Policy = AwsCustomResourcePolicy.FromSdkCalls(new SdkCallsPolicyOptions
    {
        Resources = AwsCustomResourcePolicy.ANY_RESOURCE
    })
});

getEnvironmentUrl.Node.AddDependency(apiEnvironment);

var environmentUrl = getEnvironmentUrl.GetResponseField("Environments.0.CNAME");
var hostedZone = HostedZone.FromLookup(this, "HostedZone", new HostedZoneProviderProps
{
    DomainName = "example.com"
});
var aRecord = new ARecord(this, "AliasRecord", new ARecordProps
{
    RecordName = "api.example.com",
    Zone = hostedZone,
    Target = RecordTarget.FromAlias(new ElasticBeanstalkEnvironmentEndpointTarget(environmentUrl))
});

aRecord.Node.AddDependency(getEnvironmentUrl);

Unfortunately, this produces the same exception. The only thing that helps is to hardcode the environment url

var aRecord = new ARecord(this, "AliasRecord", new ARecordProps
{
    RecordName = "api.example.com",
    Zone = hostedZone,
    Target = RecordTarget.FromAlias(new ElasticBeanstalkEnvironmentEndpointTarget("hardcoded environment url"))
});

I found in the source code that environment url doesn't support tokens. Why?

I believe if I write that in CloudFormation that would be simple !Ref to the function response value.

CDK CLI Version

2.162.1 (build 10aa526)

Framework Version

No response

Node.js Version

22.9.0

OS

windows 11 - 22H2 (22621.4317)

Language

.NET

Language Version

.net 8

Other information

No response

@ivanovvitaly ivanovvitaly added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Oct 22, 2024
@github-actions github-actions bot added the @aws-cdk/aws-route53 Related to Amazon Route 53 label Oct 22, 2024
@pahud
Copy link
Contributor

pahud commented Oct 22, 2024

I checked the document in the inline code comment

https://github.com/aws/aws-cdk/blame/f1e2f3b602899b28610849b8b55748f288f50fcd/packages/aws-cdk-lib/aws-route53-targets/lib/elastic-beanstalk-environment-target.ts#L18

I can't find any relevant restriction about it. A very quick workaround is to first provide a dummy hardcoded URL and then use escape hatches to override relevant prop with the Ref statement as you mentioned. If that workaround works, we might need a PR to get it fixed.

@pahud pahud added p2 effort/medium Medium work item – several days of effort and removed needs-triage This issue or PR still needs to be triaged. labels Oct 22, 2024
@pahud
Copy link
Contributor

pahud commented Oct 22, 2024

After revisiting the code:

This works:

target: route53.RecordTarget.fromAlias(new route53targets.ElasticBeanstalkEnvironmentEndpointTarget('mysampleenvironment.xyz.us-east-1.elasticbeanstalk.com')),

It's because for some reason a RegionInfo.get() will be invoked here, which does not support unresolved token.

https://github.com/aws/aws-cdk/blame/f1e2f3b602899b28610849b8b55748f288f50fcd/packages/aws-cdk-lib/aws-route53-targets/lib/elastic-beanstalk-environment-target.ts#L25

I don't understand why hostedZoneId has to be retrieved like that. Seems a bug to me but I am not 100% sure.

I think the hostedZoneId should be passed like this rather than looking up from RegionInfo.get()

const ar = new route53.ARecord(this, 'AliasRecord', {
      zone: hostedZone,
      target: route53.RecordTarget.fromAlias(new route53targets.ElasticBeanstalkEnvironmentEndpointTarget(
        ebenv.attrEndpointUrl,
        hostedZone.hostedZoneId, // <----
      )),
    })

And my full code should be like:

  // create a eb CfnEnvironment
    const ebenv = new eb.CfnEnvironment(this, 'EBEnvironment', {
      applicationName: 'my-app',
      solutionStackName: '64bit Amazon Linux 2 v5.5.0 running Node.js 16',
      versionLabel: '1.0.0',
      platformArn: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 16.14.2 running on 64bit Amazon Linux 2',
      optionSettings: [
        {
          namespace: 'aws:autoscaling:launchconfiguration',
          optionName: 'IamInstanceProfile',
          value: 'aws-elasticbeanstalk-ec2-role',
        },
        {
          namespace: 'aws:autoscaling:launchconfiguration',
          optionName: 'InstanceType',
          value: 't3.medium',
        },
      ],
    });

    const hostedZone = route53.PublicHostedZone.fromLookup(this, 'HostedZone', {
      domainName: 'example.com',
    });


    const ar = new route53.ARecord(this, 'AliasRecord', {
      zone: hostedZone,
      target: route53.RecordTarget.fromAlias(new route53targets.ElasticBeanstalkEnvironmentEndpointTarget(
        ebenv.attrEndpointUrl,
        hostedZone.hostedZoneId,
      )),
    })

I am making it a p1 as this doesn't seem to have a easy workaround here. Meanwhile, we welcome community PRs.

@pahud pahud added p1 and removed p2 labels Oct 22, 2024
@ivanovvitaly
Copy link
Author

ivanovvitaly commented Oct 23, 2024

@pahud Thanks for quick feedback! I like the workaround with escape hatches, unfortunately my CDK experience didn't let me think that way :)
Leaving working example

CfnEnvironment environment;

var getEnvironmentUrl = new AwsCustomResource(this, "GetEnvironmentUrl", new AwsCustomResourceProps
{
    OnUpdate = new AwsSdkCall
    {
        Service = "elasticbeanstalk",
        Action = "DescribeEnvironments",
        Parameters = new Dictionary<string, object>
        {
            ["EnvironmentNames"] = new[] { environment.EnvironmentName }
        },
        PhysicalResourceId = PhysicalResourceId.Of($"GetEnvironmentUrl-{environment.ApplicationName}-{environment.EnvironmentName}")
    },
    Policy = AwsCustomResourcePolicy.FromSdkCalls(new SdkCallsPolicyOptions
    {
        Resources = AwsCustomResourcePolicy.ANY_RESOURCE
    })
});
getEnvironmentUrl.Node.AddDependency(environment);

var hostedZone = HostedZone.FromLookup(this, "HostedZone", new HostedZoneProviderProps
{
    DomainName = "example.com"
});

var aRecord = new ARecord(this, "AliasRecord", new ARecordProps
{
    RecordName = "api.example.com",
    Zone = hostedZone,
    Target = RecordTarget.FromAlias(new ElasticBeanstalkEnvironmentEndpointTarget($"http://placeholder.placeholder.{Stack.Of(this).Region}.elasticbeanstalk.com"))
});

var recordSet = aRecord.Node.DefaultChild as CfnRecordSet;
recordSet.AliasTarget = new CfnRecordSet.AliasTargetProperty
{
    HostedZoneId = RegionInfo.Get(Stack.Of(this).Region).EbsEnvEndpointHostedZoneId,
    DnsName = getEnvironmentUrl.GetResponseField("Environments.0.CNAME")
};

which produces correct CF template

AliasRecord2A2FBE8F:
    Type: AWS::Route53::RecordSet
    Properties:
      AliasTarget:
        DNSName:
          Fn::GetAtt:
            - GetEnvironmentUrl7DB70163
            - Environments.0.CNAME
        HostedZoneId: Z117KPS5GTRQ2G # EB regional hosted zone id
      HostedZoneId: Z1VWNYUQXG1QZO # my hosted zone id
      Name: api.example.com.
      Type: A

The workaround works well, though the lib might need fixes. I'm not strong enough to help with community PR at this point, maybe later when skill up in the framework. Do you want me to close the issue?

@moelasmar moelasmar self-assigned this Oct 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-route53 Related to Amazon Route 53 bug This issue is a bug. effort/medium Medium work item – several days of effort p1
Projects
None yet
Development

No branches or pull requests

3 participants