End-to-End Example
Updated
by DE-CIX PDM Team
End-to-End Example: Deploying a Cloud ROUTER with Terraform
This example demonstrates a complete Cloud ROUTER deployment using the IX-API Terraform provider. It is intended to show how a customer might attach multiple services to a single Cloud ROUTER and manage them consistently through Terraform.
The scenario includes:
- 1 Cloud ROUTER (VRF)
- 2 Cloud VC connections to AWS from differnet POPs
- 1 redundant Cloud VC Connection to Azure
- 1 P2P VC connection to your on-prem hardware
- 1 Prefix List
- 1 Routing Policy
- 3 Static Routes to Aggregate Routes to AWS
- Data sources to identify Product Offerings and other related entities required
- Prerequisites
- Terraform v1.0 or later. It should work also with opentofu
- Valid IX-API credentials. These can be generated in the DE-CIX Portal
- A physical connection ID for the P2P VC
- BGP passwords for all sessions
Step 1 – Define Variables
variable "api_key" {
description = "API Key - can be generated via the DE-CIX Portal"
type = string
sensitive = true
}
variable "api_secret" {
description = "API Secret - can be generated via the DE-CIX Portal"
type = string
sensitive = true
}
variable "account_name" {
description = "Customer Name - as displayed in the DE-CIX Portal"
type = string
}
variable "on_prem_connection_id_fra" {
description = "On Prem Connection Id. Should be in the format of DXDB:NAS:<some-number>"
type = string
}
variable "cr_bandwidth" {
description = "Total bandwidth available to the Cloud Router"
default = 5000
}
variable "cr_asn" {
description = "ASN used on the Cloud Router"
default = 64512
}
variable "on_prem_bandwidth" {
description = "Bandwidth requested for the on-prem connection"
default = 1000
}
variable "on_prem_bgp_password" {
description = "BGP password for the P2P VC session"
type = string
sensitive = true
default = "5uperS3cure"
}
variable "aws_account_id" {
description = "AWS Account ID (e.g. 123456789012) that the hosted connection should be provided to"
default = "123123123123"
}
variable "aws_bandwidth" {
description = "Bandwidth requested for both legs of the redundant AWS Connection"
default = 500
}
variable "aws_asn" {
description = "ASN used on the AWS Cloud of the Customer"
default = 64513
}
variable "aws_bgp_password" {
description = "BGP password for AWS"
type = string
sensitive = true
default = "5uperS3cure"
}
variable "azure_service_key" {
description = "Azure ExpressRoute service key (UUID)"
default = "00000000-0000-0000-0000-000000000000"
}
variable "azure_bandwidth" {
description = "Bandwidth requested for both legs of the redundant AWS Connection"
default = 500
}
variable "azure_asn" {
description = "ASN used on the AWS Cloud of the Customer"
default = 12076
}
variable "azure_bgp_password" {
description = "BGP password for Azure"
type = string
sensitive = true
default = "5uperS3cure"
}Step 2 – Configure the Provider
terraform {
required_providers {
ixapi = {
source = "registry.terraform.io/ix-api-net/ixapi"
version = "~> 1.0"
}
}
}
provider "ixapi" {
api = "https://api.de-cix.net/api/v2"
api_key = var.api_key
api_secret = var.api_secret
extension_de_cix_cloud_router_enabled = true
}Step 3 – Set some basic data sources needed throughout the example
data "ixapi_account" "account" {
name = var.account_name # Account name as visible in the portal
}
data "ixapi_metro_area_network" "fra" {
name = "FRA"
}Step 4 – Create the Cloud ROUTER
# Get a Cloud ROUTER product offering with some amount of bandwidth and a contract period of 1 month in the desired metro area
data "ixapi_de_cix_product_offerings_cloud_vrf" "fra" {
name = "Cloud ROUTER"
bandwidth = var.cr_bandwidth_mbps # e.g. 5000
contract_period = "P1M"
service_metro_area_network = data.ixapi_metro_area_network.fra.id
}
resource "ixapi_de_cix_cloud_router" "main" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
product_offering = data.ixapi_de_cix_product_offerings_cloud_vrf.fra.product_offerings[0].id
capacity = data.ixapi_de_cix_product_offerings_cloud_vrf.fra.product_offerings[0].bandwidth_max
asn = var.cr_asn # e.g. 64512
external_ref = "terraform-e2e-example-router"
}
Step 5 – Create a Route Policy with one Prefix List
A Route policy is created that is also referencing a Prefix list. In this example it is used to only accept Prefixes within the range of the RFC1918 address ranges. Other routes received on the BGP Peers that this policy will get assigned to will be rejected.
resource "ixapi_de_cix_cloud_router_prefix_list" "rfc1918" {
name = "rfc1918"
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
match_list {
prefix = "10.0.0.0/8"
min_length = 8
max_length = 32
}
match_list {
prefix = "172.16.0.0/12"
min_length = 12
max_length = 32
}
match_list {
prefix = "192.168.0.0/16"
min_length = 16
max_length = 32
}
}
resource "ixapi_de_cix_cloud_router_policy" "inbound" {
name = "inbound-policy"
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
entries {
sequence_number = 10
match_prefix_list = ixapi_de_cix_cloud_router_prefix_list.rfc1918.name
action {
filter = "accept"
local_preference = 200
}
}
entries {
sequence_number = 20
action {
filter = "reject"
}
}
}Step 5 – Create a P2P VC and attach it to the Cloud ROUTER
A P2P VC is a Virtual Circuit that you setup to one of your ports. In this case we assume it's a VC to your on-prem infrastructure.
Multiple data and resource blocks are used to create the P2P VC:
- Product offerings for the connection within the metro area are looked up via data source
ixapi_product_offerings_p2p_vc - The Network Service
ixapi_network_service_p2p_vcis created based on the product offering - The actual connection (port) for the connection to on-prem is selected based on the NAS identifier via data source
ixapi_connections - The Network Service is getting configured via resource
ixapi_de_cix_cloud_router_network_service_config_p2p_vcproviding configuration details like VLAN, IPv4 and BGP parameters
data "ixapi_product_offerings_p2p_vc" "fra" {
bandwidth = var.on_prem_bandwidth # e.g. 100
service_metro_area_network = data.ixapi_metro_area_network.fra.id
handover_metro_area_network = data.ixapi_metro_area_network.fra.id
}
resource "ixapi_network_service_p2p_vc" "on_prem_fra" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
joining_member_account = data.ixapi_account.account.id
product_offering = data.ixapi_product_offerings_p2p_vc.fra.product_offerings[0].id
display_name = "P2P Connection from On-Prem Handover to Cloud Router"
}
data "ixapi_connections" "on_prem_fra" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
metro_area_network = data.ixapi_metro_area_network.fra.id
name = var.on_prem_connection_id_fra # "DXDB:NAS:12341234" - your physical port identifies
}
resource "ixapi_de_cix_cloud_router_network_service_config_p2p_vc" "on_prem" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
cloud_router = ixapi_de_cix_cloud_router.main.id
network_service = ixapi_network_service_p2p_vc.on_prem_fra.id
network_connection = data.ixapi_connections.on_prem_fra.connections[0].id
address = "10.0.1.1/30"
bgp_neighbor = "10.0.1.2"
bgp_neighbor_asn = 64512
bgp_password = var.on_prem_bgp_password
admin_status = "enabled"
bfd_enabled = true
external_ref = "terraform-e2e-example-on-prem"
vlan_config {
vlan_type = "dot1q"
vlan = 100
}
}Step 6 – Attach two AWS Direct Connect Connections to the Cloud ROUTER from different POPs within the Metro Area
For each connection the following blocks are used:
- Data block to get the
ixapi_product_offerings_cloud_vcbased on the service provider pop - Resource block to define the Network Service
ixapi_network_service_cloud_vcbased on the product offering - Resource block to provision the Network Service Configuration
ixapi_de_cix_cloud_router_network_service_config_cloud_vcincluding BGP and IPv4 configuration details
Note on handover: Refers to cloud side diversity. For most cloud providers the value is always 1 (primary handover). For Azure, 2 can be used for a secondary handover point.
## Link 1 from INX6
data "ixapi_product_offerings_cloud_vc" "aws_euc1_50_1" {
bandwidth = var.aws_bandwidth # e.g. 500
delivery_method = "shared"
handover_metro_area_network = data.ixapi_metro_area_network.fra.id
service_metro_area_network = data.ixapi_metro_area_network.fra.id
service_provider = "AWS"
service_provider_region = "eu-central-1"
service_provider_pop = "INX6"
}
resource "ixapi_network_service_cloud_vc" "aws_euc1_50_1" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
cloud_key = var.aws_account_id #123456789876
product_offering = data.ixapi_product_offerings_cloud_vc.aws_euc1_50_1.product_offerings[0].id
capacity = data.ixapi_product_offerings_cloud_vc.aws_euc1_50_1.product_offerings[0].bandwidth_max
external_ref = "terraform-e2e-example-aws-euc-1"
}
resource "ixapi_de_cix_cloud_router_network_service_config_cloud_vc" "aws_euc1_1" {
managing_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
cloud_router = ixapi_de_cix_cloud_router.main.id
network_service = ixapi_network_service_cloud_vc.aws_euc1_50_1.id
handover = 1
address = "10.0.2.1/30"
bgp_neighbor = "10.0.2.2"
bgp_neighbor_asn = var.aws_asn
bgp_password = var.aws_bgp_password
policy_ingress = ixapi_de_cix_cloud_router_policy.inbound.name
admin_status = "enabled"
bfd_enabled = true
external_ref = "terraform-e2e-example-aws-euc-1"
vlan_config {
vlan_type = "dot1q"
vlan = 100
}
}
## Link 2 from EqFA5
data "ixapi_product_offerings_cloud_vc" "aws_euc1_50_2" {
bandwidth = var.aws_bandwidth # e.g. 500
delivery_method = "shared"
handover_metro_area_network = data.ixapi_metro_area_network.fra.id
service_metro_area_network = data.ixapi_metro_area_network.fra.id
service_provider = "AWS"
service_provider_region = "eu-central-1"
service_provider_pop = "EqFA5"
}
resource "ixapi_network_service_cloud_vc" "aws_euc1_50_2" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
cloud_key = var.aws_account_id #123456789876
product_offering = data.ixapi_product_offerings_cloud_vc.aws_euc1_50_2.product_offerings[0].id
capacity = data.ixapi_product_offerings_cloud_vc.aws_euc1_50_2.product_offerings[0].bandwidth_max
external_ref = "terraform-e2e-example-aws-euc-2"
}
resource "ixapi_de_cix_cloud_router_network_service_config_cloud_vc" "aws_euc1_2" {
managing_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
cloud_router = ixapi_de_cix_cloud_router.main.id
network_service = ixapi_network_service_cloud_vc.aws_euc1_50_2.id
handover = 1
address = "10.0.2.5/30"
bgp_neighbor = "10.0.2.6"
bgp_neighbor_asn = var.aws_asn
bgp_password = var.aws_bgp_password
policy_ingress = ixapi_de_cix_cloud_router_policy.inbound.name
admin_status = "enabled"
bfd_enabled = true
external_ref = "terraform-e2e-example-aws-euc-2"
vlan_config {
vlan_type = "dot1q"
vlan = 100
}
}
Step 7 – Attach a redundant Azure Expressroute Circuit Connection to the Cloud ROUTER
A similiar approach as for the AWS section is used here. Please note that Azure ExpressRoute Circuits are redundant by default. Configuration of the primary Network Service Configuration is happening by specifying handover = 1 on the ixapi_de_cix_cloud_router_network_service_config_cloud_vc and the secondary Network Service Configuration is configured with handover = 2
data "ixapi_product_offerings_cloud_vc" "azure_gwc_50" {
bandwidth = var.azure_bandwidth
delivery_method = "shared"
handover_metro_area_network = data.ixapi_metro_area_network.fra.id
service_metro_area_network = data.ixapi_metro_area_network.fra.id
service_provider = "AZURE"
service_provider_region = "Europe"
service_provider_pop = "FRA METRO"
}
resource "ixapi_network_service_cloud_vc" "azure_gwc" {
managing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
cloud_key = var.azure_service_key # UUID like 00000000-0000-0000-0000-000000000000
product_offering = data.ixapi_product_offerings_cloud_vc.azure_gwc_50.product_offerings[0].id
capacity = data.ixapi_product_offerings_cloud_vc.azure_gwc_50.product_offerings[0].bandwidth_max
external_ref = "terraform-e2e-example-azure-gwc"
}
resource "ixapi_de_cix_cloud_router_network_service_config_cloud_vc" "azure-gwc-primary" {
managing_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
cloud_router = ixapi_de_cix_cloud_router.main.id
network_service = ixapi_network_service_cloud_vc.azure_gwc_50.id
handover = 1
address = "10.0.3.1/30"
bgp_neighbor = "10.0.3.2"
bgp_neighbor_asn = var.azure_asn
bgp_password = var.azure_bgp_password
policy_ingress = ixapi_de_cix_cloud_router_policy.inbound.name
admin_status = "enabled"
bfd_enabled = true
cloud_vlan = 100
external_ref = "terraform-e2e-example-azure-gwc-primary"
vlan_config {
vlan_type = "dot1q"
vlan = 100
}
}
resource "ixapi_de_cix_cloud_router_network_service_config_cloud_vc" "azure-gwc-secondary" {
managing_account = data.ixapi_account.account.id
billing_account = data.ixapi_account.account.id
consuming_account = data.ixapi_account.account.id
cloud_router = ixapi_de_cix_cloud_router.main.id
network_service = ixapi_network_service_cloud_vc.azure_gwc_50.id
handover = 2
address = "10.0.3.5/30"
bgp_neighbor = "10.0.3.6"
bgp_neighbor_asn = var.azure_asn
bgp_password = var.azure_bgp_password
policy_ingress = ixapi_de_cix_cloud_router_policy.inbound.name
admin_status = "enabled"
bfd_enabled = true
cloud_vlan = 100
external_ref = "terraform-e2e-example-azure-gwc-secondary"
vlan_config {
vlan_type = "dot1q"
vlan = 100
}
}
Step 8 – Create Static Routes
The following static routes are used to aggregate Routes that are announced to some of the created network services. In this case they are aggregating the RFC1918 private address ranges to one route, which will get advertised via the AWS connections. This might be required if a network has many Routes and otherwise hit the hard limits of maximum received routes at a cloud provider.
resource "ixapi_de_cix_cloud_router_static_route" "agg_10" {
name = "Aggregate 10.0.0.0/8"
prefix = "10.0.0.0/8"
next_hop = "aggregate"
network_service_configs = [
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_1.id,
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_2.id,
]
}
resource "ixapi_de_cix_cloud_router_static_route" "agg_172" {
name = "Aggregate 172.16.0.0/12"
prefix = "172.16.0.0/12"
next_hop = "aggregate"
network_service_configs = [
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_1.id,
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_2.id,
]
}
resource "ixapi_de_cix_cloud_router_static_route" "agg_192" {
name = "Aggregate 192.168.0.0/16"
prefix = "192.168.0.0/16"
next_hop = "aggregate"
network_service_configs = [
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_1.id,
ixapi_de_cix_cloud_router_network_service_config_cloud_vc.aws_euc1_2.id,
]
}Step 11 – Initialize, Review, and Apply
$ terraform init
$ terraform plan
$ terraform apply
Step 12 – Verify BGP Session State
After deployment, verify that the configured BGP sessions have reached the expected operational state. For some Cloud Providers configuration actions are required on Cloud side as well after the provisioning here.
A healthy session is expected to reach the Established state.
You can check this in the DE-CIX Customer Portal or via API.
$ curl --request GET \
--url 'https://api.de-cix.net/api/v3/decix-vrf-v1/network-service-configs/<network_service_config_id>/bgp-state' \
--header 'Authorization: Bearer <token>'
Possible BGP states:
State | Description |
| Session not yet started |
| Attempting TCP connection |
| TCP connection failed, retrying |
| TCP connected, BGP Open sent |
| BGP Open received, waiting for Keepalive |
| Session is fully operational ✓ |
Step 13 – Clean Up
To remove all resources managed by this example:
$ terraform destroy
Note: NSCs must be destroyed before the Cloud ROUTER. Terraform handles this automatically based on the resource dependency graph.
Notes
- This example uses illustrative IP addressing and ASNs. Replace these values with the parameters required for your environment.
- The exact network service IDs, handover values, and BGP settings depend on your provisioned services.
- Cloud VC and P2P VC resources follow similar patterns, but their required attributes differ based on the service type.