The terraform-provider-megaport or Megaport Terraform Provider lets you create and manage
Megaport's product and services using the Megaport API.
This provides an opportunity for true multi-cloud hybrid environments supported by Megaport's Software Defined Network (SDN). Using the Terraform provider, you can create and manage Ports, Virtual Cross Connects (VXCs), Megaport Cloud Routers (MCRs), MCR Prefix Filter Lists, Megaport Virtual Edges (MVEs), and Partner VXCs.
This provider is compatible with HashiCorp Terraform, and we have tested compatibility with OpenTofu and haven't seen issues.
The Megaport Terraform Provider is released as a tool for use with the Megaport API.
Important: The usage of the Megaport Terraform Provider constitutes your acceptance of the terms available in the Megaport Acceptable Use Policy and Global Services Agreement.
Documentation is published on the Terraform Provider Megaport registry and the OpenTofu Provider Megaport registry.
The preferred installation method is via the Terraform Provider Megaport registry.
For OpenTofu users, the provider is available via the OpenTofu Registry. No configuration changes are needed - use the same provider source as you would with Terraform.
The provider can be configured in the same way whether using HashiCorp Terraform or OpenTofu:
terraform {
required_providers {
megaport = {
source = "megaport/megaport"
version = "~> 1.3"
}
}
}
provider "megaport" {
# Configuration options
environment = "production"
access_key = "your-access-key"
secret_key = "your-secret-key"
accept_purchase_terms = true
}This provider has been tested with the Terraform MCP Server for AI-assisted infrastructure provisioning. See our Terraform MCP Server tutorial for a quick guide on using AI agents to generate and deploy Megaport network configurations.
The Megaport Terraform Provider now supports managing MCR prefix filter lists as individual resources, providing better lifecycle management and improved state handling compared to the previous inline approach.
# Create MCR without inline prefix filter lists
resource "megaport_mcr" "example" {
product_name = "my-mcr"
port_speed = 1000
location_id = 5
contract_term_months = 12
}
# Manage prefix filter lists as individual resources
resource "megaport_mcr_prefix_filter_list" "allow_private_ipv4" {
mcr_id = megaport_mcr.example.product_uid
description = "Allow private IPv4 networks"
address_family = "IPv4"
entries = [
{
action = "permit"
prefix = "10.0.0.0/8"
ge = 16
le = 24
},
{
action = "permit"
prefix = "192.168.0.0/16"
ge = 24
le = 32
}
]
}
resource "megaport_mcr_prefix_filter_list" "allow_private_ipv6" {
mcr_id = megaport_mcr.example.product_uid
description = "Allow private IPv6 networks"
address_family = "IPv6"
entries = [
{
action = "permit"
prefix = "fd00::/8"
ge = 48
le = 64
}
]
}# ❌ DEPRECATED: Inline prefix filter lists (will be removed in future version)
resource "megaport_mcr" "deprecated_example" {
product_name = "my-mcr"
port_speed = 1000
location_id = 5
contract_term_months = 12
# This approach is deprecated and will show warnings
prefix_filter_lists = [
{
description = "Allow private networks"
address_family = "IPv4"
entries = [
{
action = "permit"
prefix = "10.0.0.0/8"
ge = 16
le = 24
}
]
}
]
}- Individual Lifecycle Management: Each prefix filter list has its own Terraform state and lifecycle
- Better Error Handling: Failures in one list don't affect others
- Enhanced Reusability: Lists can be referenced and managed independently
- Cleaner State: Avoid complex nested object handling in Terraform state
- Import Support: Easy migration of existing lists using
terraform import
Use the data source to see what prefix filter lists you currently have:
data "megaport_mcr_prefix_filter_lists" "existing" {
mcr_id = "your-mcr-uid-here"
}
output "current_lists" {
value = data.megaport_mcr_prefix_filter_lists.existing.prefix_filter_lists
}For each existing list, create a corresponding resource:
resource "megaport_mcr_prefix_filter_list" "migrated_list_1" {
mcr_id = "your-mcr-uid-here"
description = "Copy description from existing list"
address_family = "IPv4"
entries = [
# Copy entries from existing configuration
]
}Import each existing list to avoid recreation:
terraform import megaport_mcr_prefix_filter_list.migrated_list_1 mcr-uid:prefix-list-idRemove the prefix_filter_lists attribute from your MCR resource and add a lifecycle rule:
resource "megaport_mcr" "example" {
product_name = "my-mcr"
port_speed = 1000
location_id = 5
contract_term_months = 12
# Remove or comment out the old prefix_filter_lists attribute
# prefix_filter_lists = [...]
# Add lifecycle rule to prevent drift warnings
lifecycle {
ignore_changes = [prefix_filter_lists]
}
}Run terraform plan to ensure no unexpected changes are detected.
The provider includes validation to prevent managing the same prefix filter lists through both methods simultaneously. If you attempt to use both inline and standalone management for the same MCR, you'll receive warnings about potential conflicts.
The inline prefix_filter_lists attribute in the MCR resource is deprecated and will be removed in a future version. We recommend migrating to standalone megaport_mcr_prefix_filter_list resources for better lifecycle management and improved state handling.
MCR Resource Shows Drift with Standalone Resources
When using standalone megaport_mcr_prefix_filter_list resources, you should add a lifecycle rule to your MCR resource to prevent Terraform from detecting drift on the prefix_filter_lists attribute. This is necessary because:
- The standalone prefix filter list resources manage the lists independently
- The MCR resource still reads the lists from the API, which can cause Terraform to detect "changes" even though the lists are being managed by the standalone resources
- This applies to both newly created MCRs and existing ones - the lifecycle rule tells Terraform to ignore differences in this attribute since it's being managed elsewhere
resource "megaport_mcr" "example" {
# ... configuration ...
lifecycle {
ignore_changes = [prefix_filter_lists]
}
}Why is this needed for new resources? Even when creating a new MCR alongside standalone prefix filter list resources, Terraform's refresh cycle will detect that the MCR has prefix filter lists attached (via the standalone resources), and without the lifecycle rule, it may show these as unexpected changes on subsequent plan/apply operations.
Mixed Usage Warning
If you see warnings about mixed usage, ensure you're not managing the same prefix filter lists through both inline and standalone methods simultaneously.
Import Format
When importing existing prefix filter lists, use the format mcr_uid:prefix_list_id:
# Get the MCR UID and prefix list ID from the Megaport Portal or API
terraform import megaport_mcr_prefix_filter_list.example a1b2c3d4-5678-90ef-ghij-klmnopqrstuv:1234- Use Location IDs: Always use location IDs instead of names for MCR placement (more stable)
- Validate Prefix Ranges: Ensure ge (greater than or equal) and le (less than or equal) values make sense for your prefix lengths
- Group Related Lists: Create logically grouped prefix filter lists for easier management
- Test Migrations: Always test migrations in a non-production environment first
- Document Purposes: Use descriptive names and descriptions for prefix filter lists
# Production MCR with standalone prefix filter lists
resource "megaport_mcr" "production" {
product_name = "prod-mcr"
port_speed = 2500
location_id = 1 # Use stable location ID
contract_term_months = 12
resource_tags = {
Environment = "production"
Owner = "network-team"
Purpose = "multi-cloud-connectivity"
}
lifecycle {
ignore_changes = [prefix_filter_lists]
}
}
# Allow internal corporate networks
resource "megaport_mcr_prefix_filter_list" "corporate_networks" {
mcr_id = megaport_mcr.production.product_uid
description = "Corporate internal networks"
address_family = "IPv4"
entries = [
{
action = "permit"
prefix = "10.100.0.0/16"
ge = 24
le = 28
},
{
action = "permit"
prefix = "10.200.0.0/16"
ge = 24
le = 28
}
]
}
# Allow cloud provider networks
resource "megaport_mcr_prefix_filter_list" "cloud_networks" {
mcr_id = megaport_mcr.production.product_uid
description = "AWS and Azure networks"
address_family = "IPv4"
entries = [
{
action = "permit"
prefix = "172.16.0.0/12"
ge = 16
le = 24
}
]
}
# IPv6 support for future expansion
resource "megaport_mcr_prefix_filter_list" "ipv6_networks" {
mcr_id = megaport_mcr.production.product_uid
description = "IPv6 corporate networks"
address_family = "IPv6"
entries = [
{
action = "permit"
prefix = "2001:db8:100::/48"
ge = 56
le = 64
}
]
}You don't need to do this if you're not modifying megaportgo, but if you need to modify it you can use a Go workspace to make this process easier. Take a look at this tutorial first to get familiar with how Go workspaces work, then create a workspace for local development. This will let you edit the megaportgo library while working on the Terraform Provider without needing to publish changes to Git or modify your go.mod file in the Terraform Provider with a replace statement.
go 1.22.0
use (
./megaportgo
./terraform-provider-megaport
)First, find the GOBIN path where Go installs your binaries. Your path may vary depending on how your Go environment variables are configured.
$ go env GOBIN
/Users/<Username>/go/binIf the GOBIN Go environment variable is not set, use the default path, /Users/<Username>/go/bin
Create a new file called .terraformrc in your home directory (~), then add the dev_overrides block below. Change the <PATH> to the value returned from the go env GOBIN command above.
provider_installation {
dev_overrides {
"registry.terraform.io/megaport/megaport" = "<PATH>"
}
# For all other providers, install them directly from their origin provider
# registries as normal. If you omit this, Terraform will _only_ use
# the dev_overrides block, and so no other providers will be available.
direct {}
}Once you’ve done that you can test out changes to the provider by installing it with
go install .inside of the terraform-provider-megaport folder
A suite of tested examples is in the examples directory
Contributions via pull request are welcome. Familiarize yourself with these guidelines to increase the likelihood of your pull request being accepted.
All contributions are subject to the Megaport Contributor Licence Agreement. The CLA clarifies the terms of the Mozilla Public Licence 2.0 used to Open Source this respository and ensures that contributors are explictly informed of the conditions. Megaport requires all contributors to accept these terms to ensure that the Megaport Terraform Provider remains available and licensed for the community.
When you open a Pull Request, all authors of the contributions are required to comment on the Pull Request confirming acceptance of the CLA terms. Pull Requests can not be merged until this is complete.
Megaport users are also bound by the Acceptable Use Policy.
If you are using site_code to filter locations in your Terraform configurations, you must update your code immediately or your configurations will fail.
The Megaport Location API has been upgraded to v3, and several important changes affect how you interact with location data:
# THIS WILL FAIL - site_code filtering is no longer supported
data "megaport_location" "broken_example" {
site_code = "NTT-TOK" # ❌ This will cause an error
}# ✅ RECOMMENDED: Use location ID for most reliable results
data "megaport_location" "recommended" {
id = 123 # IDs never change and are the most stable
}
# ✅ ALTERNATIVE: Use location name (may change over time)
data "megaport_location" "alternative" {
name = "NextDC B1" # Names can change, but currently supported
}
# 💡 TIP: Save the location ID for future use
output "location_id_for_nextdc_b1" {
value = data.megaport_location.alternative.id
description = "Location ID for NextDC B1 - save this for consistent future references"
}
# Then use the saved ID in future configurations:
# data "megaport_location" "stable_reference" {
# id = 5 # Use the ID from the output above
# }Step 1: Identify affected configurations
Search your Terraform files for any usage of site_code:
grep -r "site_code" *.tfStep 2: Replace with id or name
- Best option: Replace with the location
id(most stable) - Alternative: Replace with the location
name(may change over time)
Step 3: Update deprecated field usage Several location fields are now deprecated and will show warnings:
# These fields are deprecated and will show warnings:
# - site_code (also no longer available for filtering)
# - campus
# - network_region
# - live_date
# - v_router_available- Replace all
site_code = "..."filters withid = ...orname = "..." - Remove any code that depends on deprecated fields
- Test your configurations thoroughly
- Update any documentation or comments
If you need to find the location ID for a specific site code, you can:
-
Use Terraform data source: Query by name to get the ID:
data "megaport_location" "lookup" { name = "Your Location Name" } output "location_id" { value = data.megaport_location.lookup.id }
-
Use the API directly: Call
GET /v3/locationsto see all available locations -
Contact Support: Megaport support can help map site codes to location IDs
Locations for Megaport Data Centers can be retrieved using the Locations Data Source in the Megaport Terraform Provider.
In the Megaport API, data center location names and site codes might occasionally change as facilities are rebranded, merged, or updated. However, the location ID remains constant and is the most reliable way to identify a data center.
If your Terraform configurations or API scripts rely on a location name or code that changes, those integrations will fail to execute until the references are manually updated. This can lead to automation failures, deployment delays, or service disruptions.
To ensure consistency and reliability, we strongly recommend using the location ID instead of the name or site code when integrating with the Megaport API or defining resources in Terraform.
Current supported search methods:
id- RECOMMENDED (most reliable and stable)name- Alternative option (may change over time)
Examples:
# ✅ RECOMMENDED: Use ID for most reliable results
data "megaport_location" "stable_example" {
id = 5
}
# ✅ ALTERNATIVE: Use name (less stable, may change)
data "megaport_location" "name_example" {
name = "NextDC B1"
}
# 💡 TIP: Save the location ID for future use
output "location_id_for_nextdc_b1" {
value = data.megaport_location.name_example.id
description = "Location ID for NextDC B1 - save this for consistent future references"
}For the most up-to-date list of Megaport data center locations:
- Megaport Location IDs Documentation - Complete list of location IDs (dynamically updated with the API)
- Megaport API GET /v3/locations - Programmatic access to location data
When using filter criteria to select partner ports (used to connect to cloud service providers), the specific partner port (and therefore UID) that best matches your filters may change over time as Megaport manages capacity by rotating ports. This can lead to unexpected warning messages during Terraform operations even when the VXCs themselves are not being modified:
Warning: VXC B-End product UID is from a partner port, therefore it will not be changed.
This warning appears because Terraform detects a difference in the partner port UID even when applying changes unrelated to those specific VXCs.
To prevent these warnings and ensure configuration stability, we recommend explicitly specifying the product_uid in your partner port data source once your connections are established:
# Initial setup - use standard filtering to find the partner port
data "megaport_partner" "awshc" {
connect_type = "AWSHC"
company_name = "AWS"
product_name = "US East (N. Virginia) (us-east-1)"
diversity_zone = "blue"
location_id = 123
}
# After your connections are established, update your configuration
# to explicitly specify the product_uid for stability
data "megaport_partner" "awshc" {
product_uid = "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv"
# You can keep other filters for documentation purposes
# but product_uid takes precedence
}By default, when Terraform deletes resources, they are immediately cancelled in the Megaport portal. However, you may prefer to have resources marked for cancellation at the end of their current billing term instead of immediate cancellation.
The provider supports this with the cancel_at_end_of_term configuration option:
provider "megaport" {
environment = "production"
access_key = "your-access-key"
secret_key = "your-secret-key"
accept_purchase_terms = true
cancel_at_end_of_term = true # Mark resources for end-of-term cancellation
}Important notes:
- This feature is currently only supported for Single Ports and LAG Ports
- For other resource types, the option will be ignored and immediate cancellation will occur
- When
cancel_at_end_of_termis set totrue, resources will show as "CANCELLING" in the Megaport portal until the end of their billing term - Resources are removed from Terraform state as soon as the API call returns successfully, regardless of whether immediate or end-of-term cancellation is used
- If you reapply your configuration after a resource has been deleted, Terraform will create a new resource, even if the original resource is still visible in the Megaport portal with "CANCELLING" status
The Megaport Terraform Provider includes automatic Safe Delete Protection to prevent accidental deletion of resources that have active dependencies. This feature is always enabled and works at the API level to protect your infrastructure.
Safe Delete is a built-in safety mechanism that prevents you from deleting Ports, MCRs (Megaport Cloud Routers), or MVEs (Megaport Virtual Edge) that have VXCs (Virtual Cross Connects) attached to them. This prevents accidental service disruptions caused by deleting infrastructure that is actively in use.
When you attempt to delete a resource using Terraform:
- The provider sends a delete request with the
safeDelete=trueparameter - The Megaport API checks if the resource has any attached VXCs
- If VXCs are attached, the API returns an error and prevents the deletion
- If no VXCs are attached, the deletion proceeds normally
Safe Delete protection applies to:
- Single Ports - Physical network ports
- LAG Ports - Link Aggregation Group ports
- MCRs - Megaport Cloud Routers
- MVEs - Megaport Virtual Edge instances
# Create a port
resource "megaport_port" "my_port" {
product_name = "My Production Port"
port_speed = 10000
location_id = 123
contract_term_months = 12
marketplace_visibility = false
}
# Create a VXC attached to the port
resource "megaport_vxc" "my_vxc" {
product_name = "My VXC"
rate_limit = 1000
a_end {
requested_product_uid = megaport_port.my_port.product_uid
}
b_end {
requested_product_uid = megaport_mcr.my_mcr.product_uid
}
}If you try to run terraform destroy -target=megaport_port.my_port while the VXC is still attached, the operation will fail with an error:
Error: Could not delete port, unexpected error: Cannot delete product with active VXCs attached
To successfully delete resources with dependencies, delete them in the correct order:
- First: Delete all VXCs attached to the resource
- Then: Delete the Port, MCR, or MVE
# Correct order for targeted deletions
terraform destroy -target=megaport_vxc.my_vxc # Delete VXC first
terraform destroy -target=megaport_port.my_port # Then delete the port
# Or simply destroy everything (Terraform handles dependencies automatically)
terraform destroyWhen you run a full terraform destroy, Terraform automatically determines the correct deletion order based on resource dependencies, so you don't need to worry about the order.
Safe Delete protection provides several benefits:
- Prevents service disruptions - Can't accidentally delete infrastructure carrying active traffic
- Enforces proper cleanup - Forces you to delete VXCs before their parent resources
- Automatic protection - No configuration needed, always enabled
- Clear error messages - Tells you exactly why deletion failed
Safe Delete protection is different from Terraform's lifecycle { prevent_destroy = true } feature:
| Feature | Level | When It Protects | Configuration Required |
|---|---|---|---|
| Safe Delete | API/Provider | When resource has attached VXCs | None (always enabled) |
| Lifecycle prevent_destroy | Terraform | Always prevents deletion of specific resources | Yes (per resource) |
Safe Delete is automatic dependency protection, while lifecycle prevent_destroy is optional protection for critical resources you specifically designate.
For more information about using lifecycle blocks to protect critical resources, see the prevent_destroy example.
In addition to the automatic Safe Delete protection, you can use Terraform's lifecycle block with prevent_destroy = true to explicitly protect critical production resources from accidental deletion.
Use prevent_destroy for resources that are critical to your infrastructure and should never be accidentally deleted:
- Production ports carrying live traffic
- MCRs routing traffic between multiple cloud providers
- VXCs connecting to critical services
- Any resource that would be expensive or time-consuming to recreate
resource "megaport_port" "production_port" {
product_name = "Production Port - Protected"
port_speed = 10000
location_id = data.megaport_location.my_location.id
contract_term_months = 12
marketplace_visibility = false
# Lifecycle block to prevent accidental destruction
lifecycle {
prevent_destroy = true
}
}
resource "megaport_mcr" "production_mcr" {
product_name = "Production MCR - Protected"
location_id = data.megaport_location.my_location.id
contract_term_months = 12
# Lifecycle block to prevent accidental destruction
lifecycle {
prevent_destroy = true
}
}
resource "megaport_vxc" "production_vxc" {
product_name = "Production VXC - Protected"
rate_limit = 1000
contract_term_months = 12
a_end {
requested_product_uid = megaport_port.production_port.product_uid
}
b_end {
requested_product_uid = megaport_mcr.production_mcr.product_uid
}
# Lifecycle block to prevent accidental destruction
lifecycle {
prevent_destroy = true
}
}When you add prevent_destroy = true to a resource:
- ✅ Terraform allows creation and updates to the resource
- ❌ Terraform refuses to destroy the resource via
terraform destroy - ❌ Terraform refuses to destroy the resource when it's removed from configuration
- ❌ Terraform refuses targeted destroy attempts with
-target
If you attempt to destroy a protected resource, Terraform will fail with an error:
Error: Instance cannot be destroyed
Resource megaport_port.production_port has lifecycle.prevent_destroy set,
but the plan calls for this resource to be destroyed.
To destroy a protected resource:
- Remove the
lifecycle { prevent_destroy = true }block from your configuration - Run
terraform applyto update the configuration - Run
terraform destroyto destroy the resource
- Apply to all production resources - Add
prevent_destroyto all critical infrastructure - Document protection reasons - Use comments to explain why resources are protected
- Combine with Portal locking - Use Megaport Portal's service locking feature for additional protection
- Use version control - Track changes to lifecycle blocks in Git and require code review
- Separate environments - Use different Terraform workspaces for production and non-production resources
| Feature | Protection Level | When It Activates | Configuration |
|---|---|---|---|
| Safe Delete | API/Provider | When resource has attached VXCs | Automatic (always enabled) |
| prevent_destroy | Terraform | For any deletion attempt | Manual (per resource) |
| Portal Locking | Megaport Portal | Prevents all modifications | Manual (via Portal) |
For a complete example with detailed explanations, see the prevent_destroy example.
For more information about Terraform lifecycle management, see: