terraform on azure - create keyvault with private connection

Viewed 4622

Would like to get some pointers on setting up a key vault with a private connection. Looking at the examples on the TF site and other sites I put this together but it crashes.

In short, it creates the KV, assigns some policies, and then creates the private link which is in turn associated with the service endpoint. Any help would be greatly appreciated.

locals {
  prefix = "kv01am"
}
data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "sandbox" {
  name                        = "${local.prefix}-KV"
  location                    = "eastus2"
  resource_group_name         = "rg-hsc-uscodappname01-137941ad"
  enabled_for_disk_encryption = true
  tenant_id                   = data.azurerm_client_config.current.tenant_id
#  soft_delete_enabled         = true
#  purge_protection_enabled    = false

  sku_name = "standard"

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    key_permissions = [
      "get",
    ]

    secret_permissions = [
      "get",
    ]

    storage_permissions = [
      "get",
    ]
  }

  network_acls {
    default_action = "Deny"
    bypass         = "AzureServices"
  }

}
resource "azurerm_private_link_service" "example" {
  name                        = "kv-privatelink"
  location                    = "eastus2"
  resource_group_name         = "rg-hsc-uscodappname01-137941ad"

  nat_ip_configuration {
    name      = azurerm_public_ip.example.name
    primary   = true
    subnet_id = "zzzzzzzzzzzzzzzzzzzzzzzz"
  }

}
resource "azurerm_private_endpoint" "sandbox_kv" {
  name                        = azurerm_key_vault.sandbox.name
  location                    = "eastus2"
  resource_group_name         = "rg-hsc-uscodappname01-137941ad"
  #subnet_id           = azurerm_subnet.sandbox["PrivateLink"].id
  subnet_id               = "zzzzzzzzzzzzzzzz"

  private_service_connection {
    name                           = azurerm_key_vault.sandbox.name
    private_connection_resource_id = azurerm_key_vault.sandbox.id
    is_manual_connection           = false
    subresource_names = ["Vault"]
  }
}
3 Answers

Instead of creating dns record "manually" you could have a private_dns_zone_group declared.

# ============PrivateLink==========================

resource "azurerm_private_endpoint" "pe_kv" {
  name                = format("pe-2%s", var.name)
  location            = data.azurerm_resource_group.main.location
  resource_group_name = data.azurerm_resource_group.main.name
  subnet_id           = data.azurerm_subnet.main.id

  private_dns_zone_group {
    name                 = "privatednszonegroup"
    private_dns_zone_ids = [azurerm_private_dns_zone.main.id]
  }

  private_service_connection {
    name                           = format("pse-2%s", var.name)
    private_connection_resource_id = azurerm_key_vault.main.id
    is_manual_connection           = false
    subresource_names = ["Vault"]
  }
}
resource "azurerm_private_dns_zone" "main" {
  name                = "privatelink.vaultcore.azure.net"
  resource_group_name = data.azurerm_resource_group.main.name
}

This is what I ended up doing. Could not find a good way to derive the ip address for the private link endpoint so I just hard coded it, if someone has a better way to handle this that would be great, not too much literature on that subject. Also, added a section to register the A record in private DNS but beware this creates a DNS Private zone in the same subnet as the kv.

data "azurerm_resource_group" "main" {
  name = var.resource_group_name
}

data "azurerm_subnet" "main" {
  name                 = var.virtual_network_subnet_name
  virtual_network_name = var.virtual_network_name
  resource_group_name  = var.vnet_resource_group_name
}

data "azurerm_client_config" "main" {}

resource "azurerm_key_vault" "main" {
  name                = var.name
  location            = data.azurerm_resource_group.main.location
  resource_group_name = data.azurerm_resource_group.main.name
  tenant_id           = data.azurerm_client_config.main.tenant_id

  enabled_for_deployment          = var.enabled_for_deployment
  enabled_for_disk_encryption     = var.enabled_for_disk_encryption
  enabled_for_template_deployment = var.enabled_for_template_deployment
#  soft_delete_enabled         = false
#  purge_protection_enabled    = false  
  
  sku_name = var.sku  
  
  network_acls {
    default_action = "Deny"
    bypass         = "AzureServices"
#    ip_rules       = var.ip_rules
  }
  

# ============PrivateLink==========================

resource "azurerm_private_endpoint" "pe_kv" {
  name                = format("pe-2%s", var.name)
  location            = data.azurerm_resource_group.main.location
  resource_group_name = data.azurerm_resource_group.main.name
  subnet_id           = data.azurerm_subnet.main.id

  private_service_connection {
    name                           = format("pse-2%s", var.name)
    private_connection_resource_id = azurerm_key_vault.main.id
    is_manual_connection           = false
    subresource_names = ["Vault"]
  }
}
resource "azurerm_private_dns_zone" "main" {
  name                = "privatelink.vaultcore.azure.net"
  resource_group_name = data.azurerm_resource_group.main.name
}
resource "azurerm_private_dns_a_record" "pe_kv" {
  name                = var.name
  zone_name           = azurerm_private_dns_zone.main.name
  resource_group_name = data.azurerm_resource_group.main.name
  ttl                 = 300
  records             = ["1.2.3.4"]
}

output kv_private_ip {
  value =   ["1.2.3.4"]
}

This is how I get fqdn and private IP:

resource "azurerm_private_endpoint" "private_endpoint" {
  count               = var.private_link_subnet != null ? 1 : 0
  name                = "${var.private_link_subnet.virtual_network_name}-${var.name}"
  location            = var.location
  resource_group_name = var.resource_group
  subnet_id           = var.private_link_subnet.id
  private_service_connection {
    is_manual_connection           = false
    name                           = "${var.private_link_subnet.virtual_network_name}-${var.name}"
    private_connection_resource_id = azurerm_key_vault.vault.id
    subresource_names              = ["vault"]
  }
  lifecycle { ignore_changes = [tags] }
}

resource "null_resource" "dns_update" {
  triggers = {
    priv_fqdn = "${azurerm_private_endpoint.private_endpoint[0].custom_dns_configs[0].fqdn}"
    priv_ip   = "${azurerm_private_endpoint.private_endpoint[0].custom_dns_configs[0].ip_addresses[0]}"
  }

  provisioner "local-exec" {
    when    = destroy
    command = <<EOF
      echo ${self.triggers.priv_fqdn}
      bash ${path.module}/dns_update.sh destroy ${self.triggers.priv_fqdn}
    EOF
  }

  provisioner "local-exec" {
    command = <<EOF
      echo ${self.triggers.priv_fqdn}
      echo ${self.triggers.priv_ip}
      bash ${path.module}/dns_update.sh apply ${self.triggers.priv_fqdn} ${self.triggers.priv_ip}
      bash ${path.module}/dns_update.sh get ${self.triggers.priv_fqdn}
    EOF
  }
}

then I have:
self.triggers.priv_fqdn >> szp.vaultcore.azure.net
self.triggers.priv_ip >> 10.10.8.205

Related