skip to Main Content

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" {

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search