Last active
August 5, 2025 03:53
-
-
Save supersonictw/6fc62a72946c6af27383abbade22f9a1 to your computer and use it in GitHub Desktop.
Bare Metal to phpIPAM Synchronizer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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