mystack
mystack

Reputation: 5542

Azure APIM named values using terraform

I have a terraform variable as given below

   variable "named_values" {
     type = map(object({
       value              = string
       secret             = bool
       secret_id          = optional(string)
       identity_client_id = optional(string)
     }))
     default = {
       "my-named-value-1" = {
         value              = "my-plain-text-value-1"
         secret             = false
         secret_id          = ""
         identity_client_id = ""
       }
       "my-named-value-2" = {
         value              = "my-sensitive-value-2"
         secret             = true
         secret_id          = ""
         identity_client_id = ""
       }
       "my-named-value-3" = {
         value              = ""
         secret             = true
         secret_id          = "https://testvault.vault.azure.net/secrets/secret/dfa2bed047414c528ea41889da6a66b3"
         identity_client_id = "cc0d2f31-ff6d-495b-5773-ecfgh2fd6098"
       }
     }
   }

And the terraform resource for the named values is given below

        resource "azurerm_api_management_named_value" "named_value" {
          for_each            = var.named_values
          name                = each.key
          resource_group_name = local.resource_group_name
          api_management_name = var.api_management_name
          display_name        = each.key
          value               = each.value.secret && each.value.secret_id != "" ? null : each.value.value
          secret              = each.value.secret

          dynamic "value_from_key_vault" {
            for_each = each.value.secret && each.value.secret_id != "" ? [each.value] : []
            content {
              secret_id = each.value.secret_id
              identity_client_id = try(each.value.identity_client_id, null)
            }
          }
        }

My requirement is that the value of my-named-value-1 should come as plain text,and my-named-value-2 come as a secure variable and my-named-value-3 should fetch from a keyvault. But in the case of my-named-value-1 and my-named-value-2 it is working as expected. But for the my-named-value-3 it is failing on following error

        creating or updating Named Value (Subscription: "70b2-37e3-3346-8da0- 
 55e55e73fed6"
  │ Resource Group Name: "AZR-PFS-D62-0031-1234"
  │ Service Name: "cyet101003c0237750017001"
  │ Named Value: "my-named-value-3"): polling after CreateOrUpdate: executing request: 
 unexpected status 404 (404 Not Found) with error: ResourceNotFound: NamedValue not 
 found.
  │ 
  │   with 
  module.api_management_named_value.azurerm_api_management_named_value.named_value["my- 
 named-value-3"],
  │   on module/main.tf line 37, in resource "azurerm_api_management_named_value" 
  "named_value":
  │   37: resource "azurerm_api_management_named_value" "named_value" {

Upvotes: 0

Views: 563

Answers (2)

mystack
mystack

Reputation: 5542

Able to solve the issue by adding the api management identity to the key vault access policy. After adding the access policy, the terraform apply was sucessfull.

For adding the access policy I used following code

resource "azurerm_key_vault_access_policy" "test2" {
  key_vault_id = module.key_vault.id
  tenant_id    = module.api_management.identity.0.tenant_id
  object_id    = module.api_management.identity.0.principal_id
  secret_permissions = [
   "Get",
   "List",
  ]

}

Note:

The error thrown by terraform previously didn't mention anything about access policy. It's weired.

Upvotes: 0

Rui Jarimba
Rui Jarimba

Reputation: 18169

I suggest you simplify your design slightly - why not use 2 distinct variables and corresponding azurerm_api_management_named_value resources to manage the named values?

One would stored the plain text values and the other one the values from the keyvaults.

Example (not tested):

variable "named_values" {
  type = map(object({
    value  = string
    secret = bool
  }))
  default = {
    "my-named-value-1" = {
      value  = "my-plain-text-value-1"
      secret = false
    }
    "my-named-value-2" = {
      value  = "my-sensitive-value-2"
      secret = true
    }
  }
}

variable "named_values_from_keyvault" {
  type = map(object({
    secret_id          = string
    identity_client_id = string
  }))
  default = {
    "my-named-value-3" = {
      secret_id          = "https://testvault.vault.azure.net/secrets/secret/dfa2bed047414c528ea41889da6a66b3"
      identity_client_id = "cc0d2f31-ff6d-495b-5773-ecfgh2fd6098"
    }
  }
}

resource "azurerm_api_management_named_value" "named_value" {
  for_each = var.named_values

  name                = each.key
  resource_group_name = local.resource_group_name
  api_management_name = var.api_management_name
  display_name        = each.key

  value  = each.value.value
  secret = each.value.secret
}

resource "azurerm_api_management_named_value" "named_value_from_keyvault" {
  for_each = var.named_values_from_keyvault

  name                = "${each.key}-from-keyvault" # Append a suffix to avoid name conflicts if needed
  resource_group_name = local.resource_group_name
  api_management_name = var.api_management_name
  display_name        = each.key

  # when value_from_key_vault is specified, secret must also be set to true
  secret = true 

  value_from_key_vault {
    secret_id          = each.value.secret_id
    identity_client_id = each.value.identity_client_id
  }
}

Benefits:

  • Code is clearer and easier to understand - there is no need to specify optional properties in the variables and consequently use conditions to see if properties are set or not.

Upvotes: 0

Related Questions