Skip to content

Instantly share code, notes, and snippets.

@supersonictw
Last active August 5, 2025 03:53
Show Gist options
  • Save supersonictw/6fc62a72946c6af27383abbade22f9a1 to your computer and use it in GitHub Desktop.
Save supersonictw/6fc62a72946c6af27383abbade22f9a1 to your computer and use it in GitHub Desktop.
Bare Metal to phpIPAM Synchronizer
#!/bin/bash
# ipam.sh
# SPDX-License-Identifier: MIT (https://ncurl.xyz/s/Kkn2DQsNR)
#================================================================
# Bare Metal to phpIPAM Synchronizer
#
# Purpose:
# This script runs on any bare metal server or standalone host.
# It detects its own hostname, IP, and MAC address, and reports
# this information to a phpIPAM instance.
#
# Usage:
# 1. In phpIPAM, create a new Tag (e.g., "server-sync") under Administration > IP address tags. Note its ID.
# 2. Save this script on the server (e.g., /usr/local/bin/ipam.sh).
# 3. Configure the variables in the Configuration Section below.
# 4. Make it executable: chmod +x /usr/local/bin/ipam.sh
# 5. Set up a cron job to run it periodically, e.g., every 15 minutes:
# */15 * * * * /usr/local/bin/ipam.sh >> /var/log/ipam.log 2>&1
#
# Dependencies: curl, jq (Please run 'apt update && apt install -y curl jq' first)
#================================================================
# --- Configuration Section: Please modify the variables below ---
# phpIPAM API URL, without the trailing /api/
PHPIPAM_URL="http://your_phpipam_server"
# The App ID you created in phpIPAM
APP_ID="server-sync" # You can use the same App ID
# The App API Key you obtained from phpIPAM
API_KEY="YOUR_PHPIPAM_API_SECRET_KEY"
# The phpIPAM Tag ID to assign to the IP address.
# Create a tag in phpIPAM (e.g., "Bare Metal") and note its ID.
HOST_TAG_ID="4" # Example ID, please change
# A description to be set in phpIPAM for the IP address.
DESCRIPTION="IP reported by bare metal host."
# SSL certificate validation. Set to "--insecure" if phpIPAM uses a self-signed certificate.
CURL_OPTS=""
# Example: CURL_OPTS="--insecure"
# --- Script Body: Usually no changes are needed below ---
# Check for dependencies
for cmd in curl jq ip; do
if ! command -v $cmd &> /dev/null; then
echo "ERROR: Dependency '${cmd}' is not installed." >&2
exit 1
fi
done
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"
}
log "INFO: Starting self-reporting process for this host..."
# --- 1. Get Host Information ---
# Get hostname
HOSTNAME=$(hostname)
log "INFO: Detected Hostname: ${HOSTNAME}"
# Find the primary network interface (the one with the default route)
PRIMARY_IFACE=$(ip route | grep '^default' | awk '{print $5}' | head -n 1)
if [[ -z "$PRIMARY_IFACE" ]]; then
log "WARN: Could not determine primary network interface. Will scan all interfaces."
IP_ADDRESSES=$(ip -4 addr | grep -oP 'inet \K[\d.]+' | grep -v '127.0.0.1')
else
log "INFO: Detected Primary Interface: ${PRIMARY_IFACE}"
IP_ADDRESSES=$(ip -4 addr show ${PRIMARY_IFACE} | grep -oP 'inet \K[\d.]+')
fi
# Get MAC address of the primary interface
MAC_ADDRESS=$(ip link show ${PRIMARY_IFACE} | awk '/link\/ether/ {print $2}')
log "INFO: Detected MAC Address: ${MAC_ADDRESS}"
if [[ -z "$IP_ADDRESSES" ]]; then
log "ERROR: No IPv4 addresses found. Exiting." >&2
exit 1
fi
# --- 2. Interact with phpIPAM ---
API_BASE_URL="${PHPIPAM_URL}/api/${APP_ID}"
HEADERS=(-H "token: ${API_KEY}" -H "Content-Type: application/json")
# Process each found IP address
for IP in $IP_ADDRESSES; do
log "INFO: Processing IP: ${IP}"
SUBNET_INFO_RAW=$(curl ${CURL_OPTS} -s -X GET "${HEADERS[@]}" "${API_BASE_URL}/subnets/search/${IP}/")
SUBNET_ID=$(echo "${SUBNET_INFO_RAW}" | jq -r '.data[0].id // empty')
if [[ -z "$SUBNET_ID" ]]; then
log "WARN: phpIPAM did not find an encompassing subnet for IP ${IP}. Skipping."
continue
fi
log "INFO: Found encompassing Subnet ID ${SUBNET_ID} for IP ${IP}."
ADDRESS_INFO=$(curl ${CURL_OPTS} -s -X GET "${HEADERS[@]}" "${API_BASE_URL}/addresses/search/${IP}/")
ADDRESS_ID=$(echo "${ADDRESS_INFO}" | jq -r '.data[0].id // empty')
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [[ -z "$ADDRESS_ID" ]]; then
log "ACTION: IP ${IP} does not exist. Creating..."
CREATE_PAYLOAD=$(jq -n \
--arg ip "$IP" \
--arg hostname "$HOSTNAME" \
--arg mac "$MAC_ADDRESS" \
--arg desc "$DESCRIPTION" \
--argjson tagId "$HOST_TAG_ID" \
--arg lastSeen "$timestamp" \
--arg subnetId "$SUBNET_ID" \
'{ip: $ip, hostname: $hostname, mac: $mac, description: $desc, tag: $tagId, lastSeen: $lastSeen, subnetId: $subnetId}')
CREATE_RESPONSE=$(curl ${CURL_OPTS} -s -X POST "${HEADERS[@]}" --data "$CREATE_PAYLOAD" "${API_BASE_URL}/addresses/")
if [[ $(echo "$CREATE_RESPONSE" | jq -r '.success') == "true" ]]; then
log "SUCCESS: Successfully created IP ${IP} for ${HOSTNAME}."
else
log "ERROR: Failed to create IP ${IP}. Response: $(echo "$CREATE_RESPONSE" | jq '.message')" >&2
fi
else
log "ACTION: IP ${IP} exists. Updating information..."
UPDATE_PAYLOAD=$(jq -n \
--arg hostname "$HOSTNAME" \
--arg mac "$MAC_ADDRESS" \
--arg desc "$DESCRIPTION" \
--argjson tagId "$HOST_TAG_ID" \
--arg lastSeen "$timestamp" \
'{hostname: $hostname, mac: $mac, description: $desc, tag: $tagId, lastSeen: $lastSeen}')
UPDATE_RESPONSE=$(curl ${CURL_OPTS} -s -X PATCH "${HEADERS[@]}" --data "$UPDATE_PAYLOAD" "${API_BASE_URL}/addresses/${ADDRESS_ID}/")
if [[ $(echo "$UPDATE_RESPONSE" | jq -r '.success') == "true" ]]; then
log "SUCCESS: Successfully updated IP ${IP}."
else
log "ERROR: Failed to update IP ${IP}. Response: $(echo "$UPDATE_RESPONSE" | jq '.message')" >&2
fi
fi
done
log "INFO: Self-reporting process finished."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment