Skip to content
Intermediate 40 minutes

Automation & Scripting

JSON templates, jq pipelines, idempotent scripts, bulk operations, the apply command, and GitHub Actions workflows for Megaport CLI automation

Prerequisites

  • Megaport CLI installed and configured
  • Basic shell scripting (bash)
  • jq installed

The Megaport CLI is designed for automation. JSON input mode, structured JSON/CSV output, and consistent exit codes make it composable with shell scripts, CI/CD pipelines, and infrastructure-as-code tooling.


JSON template pattern

Store resource configurations as JSON files in version control. Use environment variables for values that differ between environments.

templates/port-template.json:

json
{
  "name": "${PORT_NAME}",
  "locationId": ${LOCATION_ID},
  "portSpeed": ${PORT_SPEED},
  "term": 12,
  "marketPlaceVisibility": false,
  "costCentre": "${ENVIRONMENT}"
}

Render and apply:

bash
export PORT_NAME="Sydney Primary"
export LOCATION_ID=3
export PORT_SPEED=10000
export ENVIRONMENT=production

envsubst < templates/port-template.json > /tmp/port-config.json
megaport-cli ports buy --json-file /tmp/port-config.json

For more complex templating (conditionals, loops), use jq to transform a base template:

bash
# Inject a dynamic value into a fixed template
jq --arg name "Sydney Primary" '.name = $name' base-port.json > port.json
megaport-cli ports buy --json-file port.json

Declarative provisioning with apply

The apply command provisions multiple resources from a single YAML or JSON config file — the closest the CLI gets to a built-in IaC primitive. Resources are created in dependency order (Ports → MCRs → MVEs → VXCs), and VXC endpoints can reference other resources in the same file using {{.type.name}} template syntax.

infrastructure.yaml:

yaml
ports:
  - name: Sydney Primary
    location_id: 3
    speed: 10000
    term: 12
    marketplace_visibility: false
    cost_centre: AU-NET-001
    resource_tags:
      env: production

mcrs:
  - name: Cloud Router SYD
    location_id: 3
    speed: 5000
    term: 12
    asn: 64512

vxcs:
  - name: Sydney to Cloud Router
    rate_limit: 1000
    term: 12
    a_end:
      product_uid: "{{.port.Sydney Primary}}"
      vlan: 100
    b_end:
      product_uid: "{{.mcr.Cloud Router SYD}}"
      vlan: 100

Apply it:

bash
# Validate without provisioning
megaport-cli apply -f infrastructure.yaml --dry-run

# Provision (with confirmation prompt)
megaport-cli apply -f infrastructure.yaml

# Non-interactive for CI/CD
megaport-cli apply -f infrastructure.yaml --yes

# Roll back every resource created in this run if any step fails
megaport-cli apply -f infrastructure.yaml --yes --rollback-on-failure

# JSON output for parsing the result table
megaport-cli apply -f infrastructure.yaml --output json

The {{.port.<name>}}, {{.mcr.<name>}}, and {{.mve.<name>}} template references resolve to UIDs of resources defined earlier in the same file — so you can describe a complete network in one document without manual UID copy-paste.

💡

Use --dry-run in CI

Run apply -f infrastructure.yaml --dry-run as a PR check. It validates the entire config without provisioning anything — you catch schema and dependency errors before merging.

ℹ️

`--rollback-on-failure` — all-or-nothing applies

By default, if apply fails partway through, every resource it had already created stays around — you need to clean them up manually. Adding --rollback-on-failure flips that behaviour: on the first failure, every resource provisioned during the run is deleted, and the command exits with the failure plus a list of any orphans it couldn't reverse. Use it in CI for atomic deploys.

⚠️

Apply does not provision cloud VXCs

The apply VXC schema only supports port-to-port, port-to-MCR, and port-to-MVE links — partner configs (AWS, Azure, Google, Oracle, IBM, Transit, vRouter) are not supported. For cloud connectivity, build the underlying ports/MCRs/MVEs with apply, then attach cloud VXCs with vxc buy --json-file against the partner port UID. See the full schema and limits in the Apply reference.


Export existing resources as templates

The --export flag on any get command outputs a recreatable JSON config — ready to use with buy --json. This is a fast way to clone resources or build templates from existing infrastructure.

bash
# Export an existing port's config
megaport-cli ports get <PORT-UID> --export > port-template.json

# Modify and create a new port from the template
jq '.name = "Sydney Secondary"' port-template.json > new-port.json
megaport-cli ports buy --json-file new-port.json

Works with all resource types:

bash
megaport-cli mcr get <MCR-UID> --export > mcr-template.json
megaport-cli mve get <MVE-UID> --export > mve-template.json

Scripting flags

Two flags make buy commands script-friendly:

  • --yes / -y — skip the confirmation prompt (no interactive "are you sure?" question)
  • --no-wait — return immediately after submitting the order instead of waiting for provisioning to complete
bash
# Non-interactive provisioning for CI/CD
megaport-cli ports buy --json-file port.json --yes --no-wait

Chain operations with jq

Parse JSON output from one command and feed it into the next.

bash
# Create a port, then look up its UID to use in subsequent commands
megaport-cli ports buy --json-file port.json
PORT_UID=$(megaport-cli ports list --output json | jq -r '.[] | select(.name == "Sydney Primary") | .uid')
echo "Port created: $PORT_UID"

AWS_PARTNER=$(megaport-cli partners list --company-name "Amazon Web Services" --location-id 3 --output json | jq -r '.[0].productUid')

megaport-cli vxc buy \
  --name "Sydney to AWS" \
  --a-end-uid "$PORT_UID" \
  --b-end-uid "$AWS_PARTNER" \
  --a-end-vlan 100 \
  --rate-limit 1000 \
  --term 12 \
  --b-end-partner-config '{"connectType":"AWS","ownerAccount":"123456789012","type":"private","asn":65000}'

Extract specific fields:

bash
# Get all LIVE port UIDs
megaport-cli ports list --output json | jq -r '.[] | select(.status == "LIVE") | .uid'

# Get port names and speeds as a table
megaport-cli ports list --output json | jq -r '.[] | [.name, (.speed | tostring)] | @tsv'

# Count resources by status
megaport-cli ports list --output json | jq 'group_by(.status) | map({status: .[0].status, count: length})'

Idempotency — check before creating

Avoid creating duplicate resources by checking if they already exist:

bash
check_or_create_port() {
  local name="$1"
  local config_file="$2"

  EXISTING=$(megaport-cli ports list --output json | \
    jq -r --arg name "$name" '.[] | select(.name == $name) | .uid')

  if [ -n "$EXISTING" ]; then
    echo "Port '$name' already exists: $EXISTING"
    echo "$EXISTING"
  else
    echo "Creating port '$name'..."
    megaport-cli ports buy --json-file "$config_file"
    megaport-cli ports list --output json | \
      jq -r --arg name "$name" '.[] | select(.name == $name) | .uid'
  fi
}

PORT_UID=$(check_or_create_port "Sydney Primary" port.json)

Error handling

bash
#!/bin/bash
set -euo pipefail
# set -e  → exit on error
# set -u  → exit on undefined variable
# set -o pipefail → catch failures in pipes

# Validate prerequisites
command -v jq >/dev/null 2>&1 || { echo "jq is required but not installed" >&2; exit 1; }
command -v megaport-cli >/dev/null 2>&1 || { echo "megaport-cli is required but not installed" >&2; exit 1; }

# Trap errors for cleanup
cleanup() {
  echo "Script failed — check resources for partial provisioning" >&2
}
trap cleanup ERR

# Test credentials before starting
megaport-cli locations list --output json > /dev/null || {
  echo "Authentication failed — check MEGAPORT_ACCESS_KEY and MEGAPORT_SECRET_KEY" >&2
  exit 1
}

Bulk operations

Tag all LIVE ports

bash
megaport-cli ports list --output json | \
  jq -r '.[] | select(.status == "LIVE") | .uid' | \
  while IFS= read -r uid; do
    echo "Tagging port: $uid"
    megaport-cli ports update-tags "$uid" \
      --json '{"env":"production","managed-by":"automation"}'
  done

Bulk update cost centres

bash
# Apply cost centre to all ports matching a name pattern
megaport-cli ports list --output json | \
  jq -r '.[] | select(.name | startswith("SYD")) | .uid' | \
  while IFS= read -r uid; do
    megaport-cli ports update "$uid" --cost-centre "AU-INFRA-001"
  done

Export all resources to CSV report

bash
#!/bin/bash
DATE=$(date +%Y-%m-%d)
REPORT="megaport-report-${DATE}"

megaport-cli ports list --output csv > "${REPORT}-ports.csv"
megaport-cli mcr list --output csv > "${REPORT}-mcrs.csv"
megaport-cli mve list --output csv > "${REPORT}-mves.csv"
megaport-cli vxc list --output csv > "${REPORT}-vxcs.csv"

echo "Reports generated:"
ls -lh "${REPORT}"*.csv

Health checks and monitoring

bash
# Find any non-LIVE resources (potential issues)
echo "=== Non-LIVE ports ==="
megaport-cli ports list --output json | \
  jq '.[] | select(.status != "LIVE") | {name, status, uid}'

echo "=== Non-LIVE VXCs ==="
megaport-cli vxc list --output json | \
  jq '.[] | select(.status != "LIVE") | {name, status, uid}'

# Count resources by type
echo "Ports:  $(megaport-cli ports list --output json | jq length)"
echo "VXCs:   $(megaport-cli vxc list --output json | jq length)"
echo "MCRs:   $(megaport-cli mcr list --output json | jq length)"

GitHub Actions workflow

Automate provisioning on push to main. Secrets are injected as environment variables — credentials never touch the codebase.

.github/workflows/provision-network.yml:

yaml
name: Provision Megaport Network

on:
  push:
    branches: [main]
    paths:
      - 'infrastructure/megaport/**'
  workflow_dispatch:

jobs:
  provision:
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.21'

      - name: Install Megaport CLI
        run: go install github.com/megaport/megaport-cli@latest

      - name: Verify credentials
        env:
          MEGAPORT_ACCESS_KEY: ${{ secrets.MEGAPORT_ACCESS_KEY }}
          MEGAPORT_SECRET_KEY: ${{ secrets.MEGAPORT_SECRET_KEY }}
          MEGAPORT_ENVIRONMENT: production
        run: megaport-cli locations list --output json > /dev/null

      - name: Provision infrastructure
        env:
          MEGAPORT_ACCESS_KEY: ${{ secrets.MEGAPORT_ACCESS_KEY }}
          MEGAPORT_SECRET_KEY: ${{ secrets.MEGAPORT_SECRET_KEY }}
          MEGAPORT_ENVIRONMENT: production
        run: ./infrastructure/megaport/provision.sh

      - name: Export resource report
        env:
          MEGAPORT_ACCESS_KEY: ${{ secrets.MEGAPORT_ACCESS_KEY }}
          MEGAPORT_SECRET_KEY: ${{ secrets.MEGAPORT_SECRET_KEY }}
          MEGAPORT_ENVIRONMENT: production
        run: |
          megaport-cli ports list --output json | jq '.' > artifacts/ports.json
          megaport-cli vxc list --output json | jq '.' > artifacts/vxcs.json

      - uses: actions/upload-artifact@v4
        with:
          name: megaport-state
          path: artifacts/
💡

Store credentials as GitHub Secrets

Add MEGAPORT_ACCESS_KEY and MEGAPORT_SECRET_KEY under Settings → Secrets and variables → Actions in your repository. Never put credentials in workflow files or commit them to the repo.


Team config management

Onboard new team members and share environment configurations without sharing credentials.

bash
# Export current config — credentials appear as [REDACTED]
megaport-cli config export --file team-config-template.json
# Commit team-config-template.json to your repo

# New team member: import the template, then fill in their credentials
megaport-cli config import --file team-config-template.json
megaport-cli config update-profile production \
  --access-key their_access_key \
  --secret-key their_secret_key

# Verify
megaport-cli config view

Standardise output format across the team:

bash
# Set in team config template before exporting
megaport-cli config set-default output json

What's next?