Set up Azure virtual network peering with Terraform#

This help article contains step-by-step instructions for setting up peering in Azure. See the Using VPC peering article for how to set up a Project VPC.

While most Terraform manifestos can be applied in one go, we’ll have to break this up into two steps:

  1. First, we’ll create most of the necessary resources.

  2. Then, we’ll configure the Azure provider using data from step 1 to create the last resource and connect the networks together.

Before you start#

Create an Aiven authentication token. Then, set up authentication for Azure and Azure Active Directory.

For example:

terraform {
  required_providers {
    aiven = {
      source = "aiven/aiven"
      verstion = ">= 3.8.0, < 4.0.0"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "=2.30.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=3.30.0"
    }
  }
}

provider "aiven" {
  api_token = var.aiven_api_token
}

provider "azuread" {
  client_id     = "00000000-0000-0000-0000-000000000000"
  client_secret = var.azure_client_secret
  tenant_id     = "00000000-0000-0000-0000-000000000000"
}

provider "azurerm" {
  features {}
  subscription_id = "00000000-0000-0000-0000-000000000000"
  client_id       = "00000000-0000-0000-0000-000000000000"
  client_secret   = var.azure_client_secret
  tenant_id       = "00000000-0000-0000-0000-000000000000"
}

Step 1: Create or bind the resources#

Create or bind the existing resources using terraform import by following the steps in this example:

data "aiven_project" "avn_project" {
  project = "aiven-ci-kubernetes-operator"
}

data "azurerm_subscription" "subscription" {
  subscription_id = "00000000-0000-0000-0000-000000000000"
}

resource "aiven_project_vpc" "avn_vpc" {
  project      = data.aiven_project.avn_project.project
  cloud_name   = "azure-germany-westcentral"
  network_cidr = "192.168.1.0/24"

  timeouts {
    create = "15m"
  }
}

resource "azurerm_resource_group" "resource_group" {
  location = "germanywestcentral"
  name     = "my-azure-resource-group"
}

resource "azurerm_virtual_network" "virtual_network" {
  name                = "my-azure-virtual-network"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.resource_group.location
  resource_group_name = azurerm_resource_group.resource_group.name
}

# 1. Log in with an Azure admin account
# Already done.

# 2. Create application object
resource "azuread_application" "app" {
  display_name = "my-azure-application"
  sign_in_audience = "AzureADandPersonalMicrosoftAccount"

  api {
    requested_access_token_version = 2
  }
}

# 3. Create a service principal for your app object
resource "azuread_service_principal" "app_principal" {
  application_id = azuread_application.app.application_id
}

# 4. Set a password for your app object
resource "azuread_application_password" "app_password" {
  application_object_id = azuread_application.app.object_id
}

# 5. Find the id properties of your virtual network
# Skip, we have values in the state

# 6. Grant your service principal permissions to peer
resource "azurerm_role_assignment" "app_role" {
  role_definition_name = "Network Contributor"
  principal_id         = azuread_service_principal.app_principal.object_id
  scope                = azurerm_virtual_network.virtual_network.id
}

# 7. Create a service principal for the Aiven application object
# Yes, application_id is hardcoded.
resource "azuread_service_principal" "aiven_app_principal" {
  application_id = "55f300d4-fc50-4c5e-9222-e90a6e2187fb"
}

# 8. Create a custom role for the Aiven application object
resource "azurerm_role_definition" "role_definition" {
  name        = "my-azure-role-definition"
  description = "Allows creating a peering to vnets in scope (but not from)"
  scope       = "/subscriptions/${data.azurerm_subscription.subscription.subscription_id}"

  permissions {
    actions = ["Microsoft.Network/virtualNetworks/peer/action"]
  }

  assignable_scopes = [
    "/subscriptions/${data.azurerm_subscription.subscription.subscription_id}"
  ]
}

# 9. Assign the custom role to the Aiven service principal
resource "azurerm_role_assignment" "aiven_role_assignment" {
  role_definition_id = azurerm_role_definition.role_definition.role_definition_resource_id
  principal_id       = azuread_service_principal.aiven_app_principal.object_id
  scope              = azurerm_virtual_network.virtual_network.id

  depends_on = [
    azuread_service_principal.aiven_app_principal,
    azurerm_role_assignment.app_role
  ]
}

# 10. Find your AD tenant id
# Skip, it's in the env

# 11. Create a peering connection from the Aiven Project VPC
# 12. Wait for the Aiven platform to set up the connection
resource "aiven_azure_vpc_peering_connection" "peering_connection" {
  vpc_id                = aiven_project_vpc.avn_vpc.id
  peer_resource_group   = azurerm_resource_group.resource_group.name
  azure_subscription_id = data.azurerm_subscription.subscription.subscription_id
  vnet_name             = azurerm_virtual_network.virtual_network.name
  peer_azure_app_id     = azuread_application.app.application_id
  peer_azure_tenant_id  = "00000000-0000-0000-0000-000000000000"

  depends_on = [
    azurerm_role_assignment.aiven_role_assignment
  ]
}

Step 2: Create peering in Azure#

Now create the connection using the credentials from the previous step.

Note

Terraform doesn’t support dynamic provider configuration.

In the same file, follow these steps to create the connection:

# 13. Create peering from your VNet to the Project VPC's VNet
provider "azurerm" {
  features {}
  alias                = "app"
  client_id            = azuread_application.app.application_id
  client_secret        = azuread_application_password.app_password.value
  subscription_id      = data.azurerm_subscription.subscription.subscription_id
  tenant_id            = "00000000-0000-0000-0000-000000000000"
  auxiliary_tenant_ids = [azuread_service_principal.aiven_app_principal.application_tenant_id]
}

resource "azurerm_virtual_network_peering" "network_peering" {
  provider                     = azurerm.app
  name                         = "my-azure-virtual-network-peering"
  remote_virtual_network_id    = aiven_azure_vpc_peering_connection.peering_connection.state_info["to-network-id"]
  resource_group_name          = azurerm_resource_group.resource_group.name
  virtual_network_name         = azurerm_virtual_network.virtual_network.name
  allow_virtual_network_access = true
}

# 14. Wait until the Aiven peering connection is active