stephaneey / azure-apim-extension

Full Azure API Management suite and more VSTS extension
MIT License
66 stars 49 forks source link

ERROR Create or update versioned API: You cannot call a method on a null-valued expression #96

Closed sussexrick closed 4 years ago

sussexrick commented 4 years ago

I'm seeing the same issue as closed issue #58.

This is running on Azure DevOps (online, not Server) with the following task configuration:

- task: apimversioned@4
  inputs:
    ConnectedServiceNameARM: '{service-connection-selected-from-dropdown}'
    CloudEnvironment: 'https://login.windows.net/'
    ResourceGroupName: '{resource-group-name-containing-apim-here}'
    ApiPortalName: '{apim-instance-name-here}'
    OpenAPISpec: 'v3'
    Format: 'json'
    Authorization: 'None'
    SwaggerPicker: 'Url'
    swaggerlocation: 'https://{webapp-name-here}.azurewebsites.net/{controller-name-here}/swagger/v1/swagger.json'
    targetapi: '{api-name-here}'
    version: 'v1'
    apiRevisionDescription: 'N/A'
    scheme: 'Path'
    pathapi: '{controller-name-here}'
    TemplateSelector: 'Custom'
    Custom: |
      <policies>
          <inbound>
              <base />
              <set-backend-service base-url="https://{webapp-name-here}.azurewebsites.net/{controller-name-here}" />
          </inbound>
          <backend>
              <base />
          </backend>
          <outbound>
              <base />
          </outbound>
          <on-error>
              <base />
          </on-error>
      </policies>
    MicrosoftApiManagementAPIVersion: '2019-01-01'

The task output is as follows:

Starting: apimversioned
==============================================================================
Task         : API Management - Create or Update Versioned API
Description  : API Management - Create or Update Versioned API
Version      : 4.1.2
Author       : Stephane Eyskens
Help         : API Management - Create/Update API
==============================================================================
Preparing API publishing in v3 format json using Azure API 2019-01-01

checking whether https://management.azure.com/subscriptions/{subscription-id-here}/resourceGroups/{resource-group-name-here}/providers/Microsoft.ApiManagement/service/{apim-instance-name-here}/apis/{api-name-here}v1?api-version=2019-01-01 exists
##[error]You cannot call a method on a null-valued expression.
Finishing: apimversioned

Looking at the task code at https://github.com/stephaneey/azure-apim-extension/blob/master/apimversioned/v4/apimversioned.ps1 I can see it's outputting the message on line 143 but not the one on 147. It's not calling a method between those, so it's probably throwing a WebException, and my guess is it's a 404 because otherwise it would hit the throw on line 157. It would go on to attempt the Swagger definition download and if that failed it would throw this error on line 187, $swagger=$swagger.Replace("rn","n")`.

As you can tell there's a lot of guesswork in that analysis though, and I wonder whether there's a better way I can debug it?

I get the same output for an API that I know exists as for one that doesn't. If the original error is indeed the WebException, perhaps I'm specifying the {api-name-here} incorrectly? I'm using the value you see in the Azure Portal if you click on an API and go to Settings > Name?

sussexrick commented 4 years ago

OK, I worked this out. One of the requests, probably the Swagger one, was being blocked by IP restrictions on the web app set in the Azure portal under [web app] > Networking > Access restrictions.

I'll leave the issue open in case you can improve the documentation. It would be helpful if:

sussexrick commented 4 years ago

For anyone finding this, the IPs used by Azure DevOps agents are listed at https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#agent-ip-ranges

However, I ended up running my API in a Docker container within the pipeline, downloading swagger.json from there, and specifying it as a file rather than using the URL on the Web App, avoiding the need to pass the IP restrictions.

I also discovered that the same error can be triggered by the request to the API Management API which occurs before the Swagger request. This happened when using a self-hosted build agent, but we didn't try to solve it.