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.53k stars 4.61k forks source link

azurerm_api_management_api_schema cannot be attached to azurerm_api_management_api_operation representation #3980

Closed brynjarte closed 1 year ago

brynjarte commented 5 years ago

Community Note

Terraform (and AzureRM Provider) Version

Terraform v0.11.13

Affected Resource(s)

Terraform Configuration Files

resource "azurerm_api_management_api_schema" "cdm_schema" {
  api_name            = "${azurerm_api_management_api.api.name}"
  resource_group_name = "${data.azurerm_resource_group.rg.name}"
  api_management_name = "${azurerm_api_management.api_management.name}"
  schema_id           = "123"
  content_type        = "application/json"
  value               = "${file("${path.module}/schemas/cdm.json")}"
}

resource "azurerm_api_management_api_operation" "post_new_profile" {
    representation  = [{
      content_type  = "application/json"
      type_name     = "cdm"
      schema_id     = "123"
    }]
}

Expected Behavior

By applying this the operation "post_new_profile" should have the schema attached to the operation.

Actual Behavior

The schema is not attached, and you will have to go into the operation and representation and add the schema manually.

UnoSD commented 4 years ago

Same problem here, I have also tried to hack the schema into the state file and it does not recognise it. I am going to have to remove it from the Terraform template as an idiomatic resource and deploy it as ARM.

tim-mrbl commented 3 years ago

Problem analysis

It seems to me the problem is not necessarilly in attaching the schema but more in creating it. When looking at a schema created through the portal (first schema) and through terraform (second schema) they look quite different.

{
  "value": [
    {
      "id": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/xxx/apis/xxx /schemas/602c03040b253a192c4b8ac9",
      "type": "Microsoft.ApiManagement/service/apis/schemas",
      "name": "602c03040b253a192c4b8ac9",
      "properties": {
        "contentType": "application/vnd.oai.openapi.components+json",
        "document": {
          "components": {
            "schemas": {
              "post-component-request-1": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "duration": {
                    "type": "integer"
                  },
                  "owner": {},
                  "statusID": {
                    "type": "integer"
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "id": "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.ApiManagement/service/xxx/apis/xxx /schemas/post-features-request-1",
      "type": "Microsoft.ApiManagement/service/apis/schemas",
      "name": "post-features-request-1",
      "properties": {
        "contentType": "application/vnd.oai.openapi.components+json",
        "document": {
          "value": "{\r\n    \"type\": \"object\",\r\n    \"properties\": {\r\n        \"name\": {\r\n            \"type\": \"string\"\r\n        },\r\n        \"description\": {\r\n            \"type\": \"string\"\r\n        },\r\n        \"duration\": {\r\n            \"type\": \"integer\"\r\n        },\r\n        \"owner\": {},\r\n        \"statusID\": {\r\n            \"type\": \"integer\"\r\n        }\r\n    }\r\n}"
        }
      }
    }
  ],
  "count": 2
}

Looking at the api to create apim schemas it offers two different body options properties.document.definitions (for Swagger and OpenAPI schemas) and properties.document.value (for everything else). The schema created through the portal is written into properties.document.definitions and uses application/vnd.oai.openapi.components+json as the content-type. The schema created through terraform is written into properties.document.value and uses the content-type that is specified in the template. At least using the portal or terraform it doesn't seem possible to use a schema that is written into properties.document.value.

Possible solution

It seems like the solution would be to write the definition to properties.document.definitions if the content-type is set to application/vnd.ms-azure-apim.swagger.definitions+json or application/vnd.oai.openapi.components+json (or give the option to choose manually).

Additional things and workaround

When attaching a schema to an opertion, it seems to not only be necessary to set the schma_id but also the type_name. Only when setting both properties to the schemaID the schema is correctly displayed in the apim dev portal.

To work around the terraform limitation in creating schemas i am now invoking the REST API directly using a local-exec provisioner. It has it's limitations, especially when deleting or updating schemas, since the dependecies to the operations using the schemas are not correctly resolved. But for the time being this was the best solution i could come up with.

resource "null_resource" "api_schema" {
  for_each = fileset(local.api_schemas_folder, "*")

  triggers = {
    rgname      = azurerm_resource_group.function.name
    apimgmtname = azurerm_api_management.marble.name
    apiname     = azurerm_api_management_api.marble-dev-fctn.name
    schema      = file("${local.api_schemas_folder}/${each.key}")
  }

  provisioner "local-exec" {
    command     = <<-EOT
      $ErrorActionPreference = "Stop"
      az login --service-principal -u $env:ARM_CLIENT_ID -p="$env:ARM_CLIENT_SECRET" -t $env:ARM_TENANT_ID
      $token = az account get-access-token --resource 'https://management.core.windows.net/' | ConvertFrom-Json

      $Uri = "https://management.azure.com/subscriptions/$($env:ARM_SUBSCRIPTION_ID)/resourceGroups/${self.triggers.rgname}/providers/Microsoft.ApiManagement/service/${self.triggers.apimgmtname}/apis/${self.triggers.apiname}/schemas/${split(".", each.key)[0]}?api-version=2019-12-01"
      $Body = '{
        "properties": {
          "contentType": "application/vnd.oai.openapi.components+json",
          "document": {
            "components": {
              "schemas": {
                "${split(".", each.value)[0]}": ${self.triggers.schema}
              }
            }
          }
        }
      }'
      Invoke-RestMethod -Method Put `
        -Uri $Uri `
        -Body $Body `
        -ContentType "application/json" `
        -Authentication OAuth -Token (ConvertTo-SecureString $token.accessToken -AsPlainText -Force)
      EOT
    interpreter = ["pwsh", "-Command"]
  }

  provisioner "local-exec" {
    when        = destroy
    command     = <<-EOT
      $ErrorActionPreference = "Stop"
      az login --service-principal -u $env:ARM_CLIENT_ID -p="$env:ARM_CLIENT_SECRET" -t $env:ARM_TENANT_ID
      $token = az account get-access-token --resource 'https://management.core.windows.net/' | ConvertFrom-Json

      $Uri = "https://management.azure.com/subscriptions/$($env:ARM_SUBSCRIPTION_ID)/resourceGroups/${self.triggers.rgname}/providers/Microsoft.ApiManagement/service/${self.triggers.apimgmtname}/apis/${self.triggers.apiname}/schemas/${split(".", each.key)[0]}?api-version=2019-12-01"
      Invoke-RestMethod -Method Delete `
        -Uri $Uri `
        -Headers @{
          "If-match" = "*"
        } `
        -Authentication OAuth -Token (ConvertTo-SecureString $token.accessToken -AsPlainText -Force)
      EOT
    interpreter = ["pwsh", "-Command"]
  }
}
favoretti commented 3 years ago

@tim-mrbl is this still relevant? Initial issue is logged against a very old provider version. If this issue still persist, would you mind opening a new one against a current version of terraform and provider? Thank you.

tim-mrbl commented 3 years ago

This is still relevant with

Terraform v1.0.4
on windows_amd64
+ provider registry.terraform.io/hashicorp/azurerm v2.72.0

However, your comment lead me to investigate this once again and looking at the provider source i think i know which changes would be necessary: In case the contentType is application/vnd.ms-azure-apim.swagger.definitions+json or application/vnd.oai.openapi.components+json

The source for this it the api documentation.

In addition to that I found that the import function has this differentiation, but it does not seem to work, the definitions are not imported. This has to be investigated further.

I think i will give fixing this myself a try. I have, however, never worked on a terraform provider (or Go for that matter) so we will see how far i come, but maybe i can provide a solution to this in the next couple of days.

favoretti commented 3 years ago

@tim-mrbl awesome! Feel free to join our slack for any questions or discussion and let me know if I can help. Looking forward to your first PR!

tim-mrbl commented 3 years ago

I got it to work locally, it would need some polishing before i can publish ist.

While working on it i found something that i think is a problem with the underlying api. I opened a Stackoverflow question in the hope that someone has an idea whats going on there: https://stackoverflow.com/questions/68881245/openapi-type-schemas-are-written-to-the-swagger-type-field

arkiaconsulting commented 2 years ago

It looks like #18394 may help. I'll have a look asap.

katbyte commented 1 year ago

I am going to close this as it was opened for v1.x (and last was an issue with v2.x) which are no longer actively maintained and it seems the pr #18394 might have resolved this.

If this is still an issue with v3.x of the provider please do let us know by opening a new issue, thanks!

github-actions[bot] commented 4 months ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.