hashicorp / terraform-provider-azurerm

Terraform provider for Azure Resource Manager
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs
Mozilla Public License 2.0
4.46k stars 4.54k forks source link

`azurerm_api_management_subscription` creates invalid key when `api_id` is set using `azurerm_api_management_api` data source #23399

Open red-cb opened 9 months ago

red-cb commented 9 months ago

Is there an existing issue for this?

Community Note

Terraform Version

1.5.7

AzureRM Provider Version

3.74.0

Affected Resource(s)/Data Source(s)

azurerm_api_management_subscription, azurerm_api_management_api

Terraform Configuration Files

terraform {
  required_version = "~> 1.0"
  required_providers {
    azurerm = {
      version = "~> 3"
    }
  }
}

provider "azurerm" {
  features {}
}

locals {
    rg_name = "rg-example"
    apim_name = "apim-example"
}

resource "azurerm_resource_group" "example" {
  name     = local.rg_name
  location = "UK South"
}

resource "azurerm_api_management" "example" {
  name                 = local.apim_name
  resource_group_name  = azurerm_resource_group.example.name
  location             = azurerm_resource_group.example.location
  publisher_name       = "My Company"
  publisher_email      = "company@terraform.io"
  sku_name             = "Developer_1"
}

resource "azurerm_api_management_api" "example" {
  name                = "example-api"
  resource_group_name = azurerm_resource_group.example.name
  api_management_name = azurerm_api_management.example.name
  revision            = "1"
  display_name        = "Example API"
  path                = "example"
  protocols           = ["https"]

  import {
    content_format = "swagger-link-json"
    content_value  = "http://conferenceapi.azurewebsites.net/?format=json"
  }
}

resource "azurerm_api_management_subscription" "example" {
  api_management_name = azurerm_api_management.example.name
  resource_group_name = azurerm_resource_group.example.name
  display_name        = "example-api-subscription-working"
  api_id              = azurerm_api_management_api.example.id
  state               = "active"
  allow_tracing       = false
}

data "azurerm_api_management_api" "example_not_working" {
  name                = "example-api"
  resource_group_name = local.rg_name
  api_management_name = local.apim_name
  revision            = "1"

  depends_on = [
    azurerm_api_management_api.example
  ]
}

resource "azurerm_api_management_subscription" "example_not_working" {
  api_management_name = local.apim_name
  resource_group_name = local.rg_name
  display_name        = "example-api-subscription-not-working"
  api_id              = data.azurerm_api_management_api.example_not_working.id
  state               = "active"
  allow_tracing       = false
}

Debug Output/Panic Output

{
    "statusCode": 401,
    "message": "Access denied due to invalid subscription key. Make sure to provide a valid key for an active subscription."
}

Expected Behaviour

Resource azurerm_api_management_subscription.example_not_working should create an APIM subscription.

When either the primary or secondary subscription key value is passed as an Ocp-Apim-Subscription-Key header to the APIM api endpoint a 500 error should be returned (this is fine, there's no backend setup in the example)

Actual Behaviour

Resource azurerm_api_management_subscription.example_not_working creates an APIM subscription.

When either the primary or secondary subscription key value is passed as an Ocp-Apim-Subscription-Key header to the APIM api endpoint a 401 error is returned with the message "Access denied due to invalid subscription key. Make sure to provide a valid key for an active subscription."

Only affects subscriptions when api_id is set using value from a azurerm_api_management_api data source

In the example the azurerm_api_management_subscription.example resource creates a valid subscription with working keys since the api_id is set from a resource value instead of a data source value.

Note

If the invalid subscription key is saved through the Azure Portal then the key begins to work. A subsequent terraform plan reveals that the provider attempts to postfix ;rev=1 to the value for api_id which, after applying then breaks the subscription again.

Steps to Reproduce

terraform apply Send GET request to /example/sessions with Ocp-Apim-Subscription-Key header

Important Factoids

No response

References

No response

STaegtmeier commented 5 months ago

With the new version v3.89.0 of the terraform-provider-azurerm this seems to be a general issue. According to https://github.com/hashicorp/terraform-provider-azurerm/pull/23031 which changes the id format to a specific revision and append for example the ";rev=1", the terraform resource azurerm_api_management_api returns the revision whitin the returned id (azurerm_api_management_api.example.id).

The resource azurerm_api_management_subscription expects the api_id without the tailing revision to work proberly.

A workaround we used is to add a trimsuffix for the resource:

locals {
  revision        = 1  
  revision_suffix = format(";rev=%s", local.revision)
}

resource "azurerm_api_management_api" "example" {
  ...
  revision            = local.revision
  ...
}

resource "azurerm_api_management_subscription" "example_working" {
  ...
  api_id              = trimsuffix(azurerm_api_management_api.example.id, local.revision_suffix)
  ...
}
yalmaty commented 5 months ago

We faced the same issue with upgrading to the new version 3.89.0

resource "azurerm_api_management_subscription" "subscription" {
  api_management_name = var.api_management_name
  resource_group_name = var.resource_group_name
  display_name        = azurerm_api_management_api.api.name
  api_id              = azurerm_api_management_api.api.id 

  lifecycle {
    ignore_changes = [api_id]
  }
}
tgolly commented 5 months ago

Also hitting this issue since v3.89.0, the new app_id format is forcing all our azurerm_api_management_subscription to be re-created.

We had to use the same ignore_changes hack as @yalmaty to stop it.

tam-pham-ts commented 3 months ago

It's a good workaround solution. Thanks @yalmaty. :)

ausarb commented 3 months ago

We faced the same issue with upgrading to the new version 3.89.0

resource "azurerm_api_management_subscription" "subscription" {
  api_management_name = var.api_management_name
  resource_group_name = var.resource_group_name
  display_name        = azurerm_api_management_api.api.name
  api_id              = azurerm_api_management_api.api.id 

  lifecycle {
    ignore_changes = [api_id]
  }
}

The app_id was invalid with the ;rev=1 so while the lifecycle workaround works for existing deploys, wouldn't this break on a fresh install as it would get the wrong api_id from the start?

eflavio commented 2 weeks ago

I use replace with regex pattern "/;rev=.*/" on api_id to strip the revision info from the api_id:

resource "azurerm_api_management_subscription" "api_subscription" {
  ...
  api_id       = replace(azurerm_api_management_api.apim_api[each.value.api_id].id, "/;rev=.*/", "")
  ...
}