Skip to content

Terraform – Connecting 3rd parties with VNET peering

I was looking for a way of deploying 3rd party VNET peering with TF, all of the authorization steps so this post explain how to achieve this. Let’s start with explaining why I needed this.


There are many 3rd parties which we need to provide connectivity to and which don’t allow us to deploy Aviatrix spoke GW there. They are not under our subscription and we might be quite limited with options to connect to.

What could we do then to provide such connectivity:

  • Establish IPSEC tunnel
    [remote end might start complaining about extra cost of VNG or it is not their standard …]
  • Create Private Endpoint along with Private Link
    but what if traffic needs to be initiated both directions?
  • Create VNET peering

    That seems to be easy and straightforward.

The winner is?

Today let’s focus on option number 3 and as we want to do it with IaC (Infrastructure as Code) approach let’s do it with Terraform.

Before we jump into HOW to accomplish our peering we need to think about where we want to peer our 3rd party to. We use Aviatrix infrastructure so SPOKE seems to be the right choice. 
Should we have a dedicated spoke per PEER or a single one?

Dedicated spokes

What do we have here. Internal Load Balancer in each one of the spoke. We use it to provide High Resiliency as 3rd parties need to configure UDR with next hop type of NVA which should be a frontend IP of LB. Backend pool has 2 members there (spoke and spoke-ha gateways). In this scenario we are able to extend “network domains” – this is a feature of Aviatrix which allows us to isolate traffic from one domain to another (kinda VRF lite from legacy networking world). Then we can create policy saying rest of the spokes are able to talk to 3rd parties spokes but these are not allowed to talk to each other.

Shared Services approach

We accomplish:

(all of them would be in one SEGMENT). If different departments need to talk to different 3rd party then we have to use something else to accomplish that (Microsegmentation per CIDR perhaps, another option would be to use L4 FW on spoke but either we need to remove LB and HA setup or do SNAT on it to make sure return traffic always hits the same spoke GW – I tested it but that is not the main topic of this post so I will leave it there).

Might not be a problem if we enable High Performance Encryption [HPE] between Aviatrix Spoke and Transit

Different Tenant – VNET peering – HOW TO

For testing I’m using 2 different TENANTs 

  • TENANT A – my company’s subscription – AVX 
  • TENANT B – my private account – as 3rd party (PRIV-pkonitz)

We will create a dedicated service principle just for that VNET. It has to be multitenant and we have to specify the URL link for authentication.

We have created a dedicated service principle which our contractors (3rd parties) will use so we should make sure it is secured. One way of doing it is to limit the scope just to our own VNET (this is our side)

To Authorize this APP they need to go to this URL and replace these 2 relevant fields< Tenant B ID >/oauth2/authorize?client_id=< APP ID of Tenant A > &response_type=code&


Tenant B ID – this is 3rd party tenant ID

Application ID – our application ID under our tenancy

STEP 6 - Terraform code

For all AzureRM resources we used our main azurerm provider but as we have dedicated service Principle which is then shared with 3rd party we need to define 2 new providers and use aliases

provider “azurerm” {
  alias                = “corporate”
  subscription_id      = “b314xxxxxxxe6bbc29c23ce”       # sub id of tenant A
  client_secret        = “Vkxxxxxxxxxxxxxxxx~V”          # secret of service principal-TenantA
  client_id            = “936c0xxxxxf4b13919c”           # client id of service principal-TenantA
  tenant_id            = “4780xxxxxxxxxxxxfdad8493a4b6”  # tenant id of tenant A
  auxiliary_tenant_ids = [“153cexxxxxxxxxxxx446a1cba20”] # tenant id of tenant B
  features {}
provider “azurerm” {
  alias                = “thirdparty”
  subscription_id      = “3e4d6exxxxxx4cd6906f011”        # sub id of tenant B
  client_secret        = “Vkxxxxxxxxxxxxxxxx~V”           # secret of service principal-TenantA
  client_id            = “936c0xxxxxf4b13919c”            # client id of service principal-TenantA
  tenant_id            = “153cexxxxxxxxxxxx446a1cba20”    # tenant id of tenant B
  auxiliary_tenant_ids = [“4780xxxxxxxxxxxxfdad8493a4b6”] # tenant id of tenant A
  features {}
  skip_provider_registration = true
resource “azurerm_virtual_network_peering” “corporate_to_3rdparty” {
  provider                  = azurerm.corporate
  name                      = “corporate-spoke-to-xyz”
  resource_group_name       = “rg-av-KEY-AZ-spoke-1-240164”
  virtual_network_name      = “KEY-AZ-spoke-1”
  remote_virtual_network_id = “/subscriptions/3e4d6e35 … long id … /privVNET”

resource “azurerm_virtual_network_peering” “thirdparty_to_corporate” {
  provider                  = azurerm.thirdparty
  name                      = “xyz-to-corporate-spoke”
  resource_group_name       = “privRG”
  virtual_network_name      = “privVNET”
  remote_virtual_network_id = “/subscriptions/b314 … long id … KEY-AZ-spoke-1”

And the final result:

Provider registration - what is it for?
skip_provider_registration = true       # ???

I ran into one challenge when trying to make it work. Because our service-principle doesn’t have full ranging access to Private subscription (we wanted to limit it for security reasons) our TF might crash with the following error:

Adding that option solves the issue

Leave a Reply

Your email address will not be published. Required fields are marked *