serverless-application-model icon indicating copy to clipboard operation
serverless-application-model copied to clipboard

Handling AWS::NoValue in Api resources

Open abstiles opened this issue 5 years ago • 12 comments

Description:

I was hoping to make use of the new Domain property on Api resources, but conditionally based on a template parameter. Attempting to set the Domain to !Ref AWS::NoValue (for the case where no DomainName is desired) fails with a message that suggests it's not removing the Domain property and is expecting real configuration in it. Here's what I tried in the Api configuration:

Domain:
  !If
  - HasDomainName
  - DomainName: !Ref DomainName
    CertificateArn: !Ref DomainCertificateArn
  - !Ref AWS::NoValue

And the message I got:

Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [EventApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided

Steps to reproduce the issue:

  1. Create an AWS::Serverless::Api resource with Domain property specified as in above example with HasDomainName condition (evaluating to false).
  2. Deploy the SAM template.
  3. Check the results.

Observed result:

Deploy fails with message: "Custom Domains only works if both DomainName and CertificateArn are provided"

Expected result:

DomainName and BasePathMapping resources created as if I had applied the conditional on each of them, such as:

ApiCustomDomain:
  Type: AWS::ApiGateway::DomainName
  Condition: HasDomainName
  Properties:
    DomainName: !Ref DomainName
    RegionalCertificateArn: !Ref DomainCertificateArn
    EndpointConfiguration:
      Types:
        - REGIONAL

ApiBasePath:
  Type: AWS::ApiGateway::BasePathMapping
  Condition: HasDomainName
  Properties:
    DomainName: !Ref ApiCustomDomain
    RestApiId: !Ref EventApi
    Stage: !Ref EventApi.Stage

abstiles avatar Feb 06 '20 19:02 abstiles

Any progress with this? Is there a workaround other that manually setting the domain name and certificate in the AWS console for those you want it?

teemuniiranen avatar Aug 17 '20 10:08 teemuniiranen

I think the same is true for Auth / ResourcePolicy as well.

teemuniiranen avatar Sep 24 '20 13:09 teemuniiranen

I tried this every way I could think of:

  • Moving the conditional to each function (RestApiId does not support AWS::NoValue)
  • Adding all Parameters under the conditional
  • Using a mix of Mappings and higher level conditionals

We ended up needing to add a (manual or scripted) pre-deploy step that just removes the entire Domain config part if a domain isn't desired. I'd love to see a way to conditionally set a domain on an API.

nicovalencia avatar Oct 20 '20 21:10 nicovalencia

Thanks a lot guys. I was able to reproduce the issue. the problem is in this peace of code. We raise an exception if the Domain is provided but without DomainName or CertificateArn. When passing AWS::NoValue we receive a Domain={'Ref': 'AWS::NoValue'} that is why the exception is raised.

I'm labeling this for internal review as this is a common issue affecting many other areas as well.

elbayaaa avatar Feb 12 '21 00:02 elbayaaa

I believe the error extends beyond just AWS::NoValue. I am trying to conditionally set the Domain configuration to support a Gov Cloud deployment and get the same error despite each option in the condition containing the DomainName and CertificateARN. Template below for reference:

      Domain:
        !If
        - GovCloudCondition
        - DomainName: !Ref DomainName
          CertificateArn: !Ref CertificateARN
          EndpointConfiguration: REGIONAL
        - CertificateArn: !Ref CertificateARN
          DomainName: !Ref DomainName
          EndpointConfiguration: EDGE

That yields the same error above:

[Error found when transforming the template] Error transforming template: Resource with id [SageMakerApi] is invalid. Custom Domains only works if both DomainName and CertificateArn are provided

holt-calder avatar Feb 16 '21 16:02 holt-calder

Hi, is there any new on this? I am writing a SAM template, and am forced to define three AWS::Serverless::Api resources conditionally to handle cases such as:

  • The user wants to specify a vpc endpoint or not (create PRIVATE/REGIONAL API)
  • The user wants to specify a custom domain name or not

Ideally I would like to define a single resource and conditionally add a Domain or an Auth/ResourcePolicy, etc.

ermanno avatar May 19 '21 08:05 ermanno

Unfortunately, no progress I'm aware of at the time. Conditionally defining AWS::Serverless::Api resources seems to be the current work around. I think if you define your template in yaml, you can eliminate some of the duplication by using "Anchors" and "References".

elbayaaa avatar May 19 '21 17:05 elbayaaa

Hi @elbayaaa I am curious how you are able to conditionally define multiple AWS::Serverless::Api and select between them based on issue #1470. If I have an AWS::Serverless::Function that uses the REST API for events, and I don't want one to use a domain (for example), do I have to define multiple lambda functions, one for each environment?

plondino avatar Jun 15 '21 00:06 plondino

Having the same issue when defining conditional domains. Any workaround?

singhsoldier13 avatar Jul 08 '21 19:07 singhsoldier13

Having a similar issue as holt-cader trying to define conditional EndpointConfiguration. Intrinsic functions do not work inside Domain per AWS support. Hoping this gets resolved.

"""In the above snippet you are using “EndpointConfiguration” which further holds the condition under “DomainName” . Domain property was unable to to accept value through Intrinsic Function ’If' and from the testing I have performed

It seems there is no issue with your template but this feature is not being supported by AWS till now."""

   Domain:
      DomainName: www.example.com
      CertificateArn: !Sub 'my:::arn'
      EndpointConfiguration:
        !If
          - IsCA
          - EDGE
          - REGIONAL

"FAILED" Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [FormApiGateway] is invalid. EndpointConfiguration for Custom Domains must be one of ['EDGE', 'REGIONAL', 'PRIVATE']."

mclounie avatar Aug 06 '21 16:08 mclounie

I thought I found a work-around but actually found another bug. I created 2 APIs so the entire API definition was dependent on my condition. Unfortunately, the following fails validation with sam validate:

  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Events:
        MyApi:
          Type: Api
          Properties:
            RestApiId: !If [ IsNotDev, !Ref ValidApiRef1, !Ref ValidApiRef2 ]
            Path: /site-customization
            Method: POST

Here is the error:

Error: [InvalidResourceException('MyFunction', 'Event with id [MyApi] is invalid. Api Event must reference an Api in the same template.')] ('MyFunction', 'Event with id [MyApi] is invalid. Api Event must reference an Api in the same template.')

foxbox-doug avatar Feb 03 '22 23:02 foxbox-doug

I faced the same problem for an HTTP API and found a work-around which seems to be fine: set the custom domain using "native" CloudFormation resources in place of SAM "Domain" attribute. I don't know if something similar can also work for REST APIs, I post it here anyway as this topic appears to be the most related to my problem.

Resources:

  Api:
    Type: AWS::Serverless::HttpApi
    # put any necessary properties excluding the domain
  
  ApiDomain:
    Type: AWS::ApiGatewayV2::DomainName
    Condition: HasDomainName
    Properties:
      DomainName: !Ref DomainName
      DomainNameConfigurations:
        - CertificateArn: !Ref DomainCertificateArn
  
  ApiDomainMapping:
    Type: AWS::ApiGatewayV2::ApiMapping
    Condition: HasDomainName
    Properties:
      ApiId: !Ref Api
      Stage: $default  # unless a different StageName is used in Api
      DomainName: !Ref ApiDomain

rrobby86 avatar Mar 04 '22 12:03 rrobby86

You might be able to get this to work by adding AWS::LanguageExtensions to Transform as such:

Transform:
  - AWS::LanguageExtensions
  - AWS::Serverless-2016-10-31

AWS::LanguageExtensions resolves intrinsic functions if the value is known when Transforms are run.

See https://github.com/aws/serverless-application-model/issues/2533 for more information.

hoffa avatar Oct 17 '22 21:10 hoffa

Closing in favor of https://github.com/aws/serverless-application-model/issues/2533.

hoffa avatar Nov 03 '22 23:11 hoffa