3

I have a PowerShell script that creates an Azure API Management service with a custom HTTPS domain using an SSL certificate stored in an Azure key vault. I'm converting it to Terraform but running into a problem.

Terraform creates the APIM with a ‘System Assigned’ identity, and the ID needs to be granted permissions in the key vault to access the SSL cert so APIM can be created with the custom domain. However, Terraform can’t grant the ID access to key vault until APIM has been created, but APIM can’t be created until the ID has been granted access to key vault.

Relevant snippet:

resource "azurerm_api_management" "apim" {
  name                 = var.apim_name
  resource_group_name  = var.rg_name
  location             = var.location
  publisher_name       = var.publisher_name
  publisher_email      = var.publisher_email
  sku_name             = var.sku_name_capacity
  virtual_network_type = var.vnet_type

  hostname_configuration {
    dynamic "proxy" {
      for_each = var.gateway_endpoint_custom_domains
      content {
        host_name    = proxy.value
        key_vault_id = var.ssl_cert_secret_id
      }
    }
  }

  identity {
    # User assigned identities are not supported yet
    type = "SystemAssigned"
  }

  virtual_network_configuration {
      subnet_id = var.subnet_id
  }
}

resource "azurerm_key_vault_access_policy" "apim_said" {
  key_vault_id       = var.kv_id
  object_id          = var.apim_said_principal_id
  tenant_id          = data.azurerm_client_config.current.tenant_id
  secret_permissions = [
    "Get",
    "List"
  ]
}

In PowerShell, this can be done with three steps in the one script:

  1. Create APIM with its ‘System Assigned’ identity
  2. Grant the ID permissions in key vault so APIM can access the SSL cert
  3. Modify APIM to add the custom HTTPS domain

As far as I understand, Terraform doesn’t have the ability to perform these three steps in a single step because Terraform wants to create the resource as a whole rather than create & modify later.

Can Terraform be used similar to the PowerShell script by using modules? I noticed in Azure Portal that User Assigned Identities are in preview but Terraform does not support this as yet.

If multiple Terraform runs is the only option, what’s the ‘proper’ way to structure and trigger subsequent runs using Terraform Cloud? The only way that would work, as far as I can tell, is if I had two Terraform Cloud workspaces with a shared state, but I hope there’s a more elegant solution.

wertyq
  • 333
  • 1
  • 2
  • 12
  • I do not see anywhere in your config where exported resource attributes from either resource are provided as inputs to the other resource, so it is unclear how the circular dependency exists within the config. Could you please update the question for that? – Matthew Schuchard Sep 23 '21 at 13:06
  • What inputs are you expecting to see? The circular dependency exists as an inherent part of API Management and key vault access. TBH it's pretty clear if you've ever worked with APIM. – wertyq Sep 23 '21 at 23:19
  • @MattSchuchard - The APIM resource requires access to key vault to get the SSL cert (inside the hostname_configuration block), to do this APIM's identity needs to be given access to key vault to do this (hence the azurerm_key_vault_access_policy). But the APIM identity doesn't exist until APIM is created, and it can't be created without access to key vault. Hence, the circular dependency. – wertyq Sep 23 '21 at 23:25

1 Answers1

3

To create APIM, giving access to Keyvault, and setting the domain settings, you can use something like below to resolve the issue:

provider "azurerm"{
features{}
}

data "azurerm_client_config" "current" {}

resource "azurerm_api_management" "apim" {
  name                 = "ansuman-apim"
  resource_group_name  = "resourcegroup"
  location             = "West US 2"
  publisher_name       = "company"
  publisher_email      = "ansumanbal@xyx.com"
  sku_name             = "Developer_1"

  identity {
    # User assigned identities are not supported yet
    type = "SystemAssigned"
  }
}

resource "azurerm_key_vault_access_policy" "apim_said" {
  key_vault_id       = "/subscriptions/b83c1ed3-xxxx-xxxxx-xxxxx-xxxx/resourceGroups/resourcegroup/providers/Microsoft.KeyVault/vaults/testkeyvautlansuman007"
  object_id          = azurerm_api_management.apim.identity[0].principal_id
  tenant_id          = data.azurerm_client_config.current.tenant_id
  secret_permissions = [
    "Get",
    "List"
  ]
}

resource "null_resource" "previous" {}

resource "time_sleep" "wait_60_seconds" {
  depends_on = [azurerm_key_vault_access_policy.apim_said]

  create_duration = "60s"
}

resource "azurerm_api_management_custom_domain" "example" {
  api_management_id = azurerm_api_management.apim.id

  proxy {
    host_name    = "ansuman.xyz.com"
    key_vault_id = "https://testkeyvautlansuman007.vault.azure.net/secrets/test/3a6ebe74364xxxxxxxxxxx"
  }

depends_on = [
  time_sleep.wait_60_seconds
]
}

Note: Due to this drawback of azurerm_api_management not able to access keyvault while creation from Terraform, azurerm_api_management_custom_domain was added. So, that you can create the APIM with identity and give access to keyvault and then use the custom domain block to add the host_configurations to the APIM that you have created.

Output:

Enter image description here

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ansuman Bal
  • 9,705
  • 2
  • 10
  • 27
  • 1
    I don't know how I missed azurerm_api_management_custom_domain in the docs but that works perfectly. – wertyq Sep 27 '21 at 01:54
  • Shouldn't `features{}` be indented? It is in [this answer](https://stackoverflow.com/questions/69247761/can-i-perform-consecutive-changes-on-resource-with-terraform/69264545#69264545). – Peter Mortensen Mar 30 '22 at 12:24