@@ -1,4 +1,3 @@
# Powershell refuses to connect to the Netbox API on our setup without this.
add-type @"
using System.Net;
@@ -14,139 +13,291 @@ add-type @"
[System.Net.ServicePointManager ]::CertificatePolicy = New-Object TrustAllCertsPolicy
[Net.ServicePointManager ]::SecurityProtocol = [Net.SecurityProtocolType ]::Tls12
# #########################
# Fill out these settings
# #########################
# Some useful settings.
$token = " <netbox token>"
$uri = " http://netbox.domain.tld/api"
$hyperVHost = " hyper-v-host.domain.tld"
# These are some ID's in Netbox
# We added a "Hyper-V" cluster type which is id 4 . This needs to match yours!
# Type role 25 for us is "Server".
$NetboxHyperVClusterType = 4
# We added a "Hyper-V" (id 3) cluster type . This needs to match yours!
# id 25 for us corresponds to the role "Server".
$NetboxHyperVClusterType = 3
$NetboxServerRoleID = 25
# Set API Headers
$headers = New-Object " System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add (" Authorization" , " Token $token " )
$headers.Add (" Content-Type" , ' application/json' )
$headers.Add (" Accept" , ' application/json' )
# Source: https://www.reddit.com/r/PowerShell/comments/8u14wl/check_a_list_of_ips_against_a_list_of_subnets/e1brhe3/
function Test-IPInSubnet {
[CmdletBinding ()]
param (
[Parameter (
Position = 0 ,
Mandatory ,
ValueFromPipelineByPropertyName
)][ValidateNotNull ()][System.Net.IPAddress ]$Subnet ,
[Parameter (
Position = 1 ,
Mandatory ,
ValueFromPipelineByPropertyName
)][Alias (' Mask' )][ValidateNotNull ()][System.Net.IPAddress ]$SubnetMask ,
[Parameter (
Position = 0 ,
Mandatory ,
ValueFromPipeline ,
ValueFromPipelineByPropertyName
)][Alias (' Address' )][ValidateNotNull ()][System.Net.IPAddress ]$IPAddress
)
process {
$Subnet.Address -eq ($IPAddress.Address -band $SubnetMask.Address )
}
}
function Add-NetboxIP {
[CmdletBinding ()]
param (
[Parameter ()][System.Net.IPAddress ]$ip ,
[Parameter ()][String ]$mask
)
$id = Get-NetboxIP - ip $ip - mask $mask
if (-not $id ) {
# Add the IP and return the resulting Id
Write-host " [$ ( $vm.name ) ] Adding new ip address object for $ip ."
$ipconcat = $ip + " /" + " $mask "
$id = ((Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/" - Method POST - Headers $headers - Body $ (@ {address = $ipconcat } | Convertto-json )).content | convertfrom-json ).id
}
return $id
}
function Add-NetboxVM {
[CmdletBinding ()]
param (
[Parameter ()][Microsoft.HyperV.PowerShell.VirtualMachine ]$vm ,
[Parameter ()][String ]$roleId ,
[Parameter ()][String ]$clusterId
)
# Get or add the VM to Netbox
$id = Get-NetboxVM - name $vm.name
$virtualMachineBody = $ (@ {
name = $ ($vm.VMName )
cluster = $clusterId
status = $ (if ($vm.state -eq " Running" ) {" active" } else {" offline" })
role = $roleId
vcpus = $ ($vm.ProcessorCount )
memory = $ (if ($vm.DynamicMemoryEnabled ){$vm.MemoryMaximum / 1 MB }else {$vm.MemoryStartup / 1 MB })
disk = $ (((($vm.HardDrives | Get-VHD - computername $hyperVHost ).Size) | Measure-Object - Sum).Sum/ 1 GB )
} | ConvertTo-Json )
# If we're not in a cluster, we use "Standalone - $Hostname" as a cluster name instead.
# We try to see if the cluster exists, if it does, we take it's ID for later. If not, we create it.
echo - foregroundcolor cyan " [$ ( hostname) ] Trying to determine if server is part of a cluster..."
$clusterName = (Get-Cluster - Domain $ ($env: USERDNSDOMAIN ) | Get-ClusterNode | where {$_.Name -eq $ (hostname)}).Cluster.name
if (-not $id ) {
# Create
Write-host " [$ ( $vm.name ) ] Creating new VM."
$id = ((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/" - Method POST - Body $virtualMachineBody - Headers $headers ).content | convertfrom-json ).id
} else {
# Update
Write-host " [$ ( $vm.name ) ] Updating existing VM with id $id ."
$id = ((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/$id /" - Method PATCH - Body $virtualMachineBody - Headers $headers ).content | convertfrom-json ).id
}
return $id
}
function Add-NetboxNetworkInterface {
[CmdletBinding ()]
param (
[Parameter ()][Microsoft.HyperV.PowerShell.VMNetworkAdapter ]$networkAdapter ,
[Parameter ()][String ]$virtualMachineId
)
$id = Get-NetboxNetworkInterface - networkAdapter $networkAdapter - virtualMachineId $virtualMachineId
if ($clusterName -eq $null ) {
Write-Host - foregroundcolor cyan " [$ ( hostname) ] Host is standalone"
$clusterName = " Standalone-$ ( hostname) "
if (-not $id ) {
$id = ((Invoke-WebRequest - Uri " $uri /virtualization/interfaces/" - method Post - Headers $headers - body $ (@ {name = $networkAdapter.name ;virtual_machine = $virtualMachineId ;mac_address = $networkAdapter.macaddress } | Convertto-json )).content | convertfrom-json ).id
}
return $id
}
function Add-NetboxIPNetworkInterfaceAssociation {
[CmdletBinding ()]
param (
[Parameter ()][String ]$networkInterfaceId ,
[Parameter ()][String []]$ipList
)
foreach ($ipId in ($ipList | Where-Object {($null -ne $_ ) -and ($_ -notlike " fe80*" )})) {
Write-host " [$ ( $vm.name ) ] Creating interface $networkInterfaceId association with ip $ipId ."
$result = Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/$ipId /" - Method Patch - Headers $headers - Body $ (@ {assigned_object_type = " virtualization.vminterface" ;assigned_object_id = $networkInterfaceId }| ConvertTo-Json )
}
}
function Add-NetboxCluster {
[CmdletBinding ()]
param (
[Parameter ()][String ]$name
)
Write-Host - foregroundcolor yellow " [$name ] The cluster doesn't exist. Let's add it !"
return ((Invoke-WebRequest - Uri " $uri /virtualization/clusters/" - Method Post - Body $ (@ {name = $name ;type = $NetboxHyperVClusterType }| Convertto-json ) - Headers $headers ).content | convertfrom-json ).id
}
function Get-NetboxCluster {
[CmdletBinding ()]
param (
[Parameter ()][String ]$name
)
Write-host " [$name ] Looking for a matching cluster."
try { return ((Invoke-WebRequest - Uri " $uri /virtualization/clusters/?name=$name " - Method Get - Headers $headers ).content | convertfrom-json ).results[0 ].id }
catch {
Write-host " [$name ] No match found."
return $null
}
}
function Get-NetboxVM {
[CmdletBinding ()]
param (
[Parameter ()][String ]$name
)
Write-host " [$ ( $vm.name ) ] Looking for a matching VM."
try { return ((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/?q=$name " - Method Get - Headers $headers ).content | convertfrom-json ).results[0 ].id }
catch {
Write-host " [$ ( $vm.name ) ] No match found"
return $null
}
}
function Get-NetboxIP {
[CmdletBinding ()]
param (
[Parameter ()][System.Net.IPAddress ]$ip ,
[Parameter ()][String ]$mask
)
$clusterGet = ( Invoke-WebRequest - Uri " $uri /virtualization/clusters/?name= $clusterName " - Method Get - Headers $headers ).content | convertfrom-json
$ipconcat = " $ip " + " / " + " $mask "
if ($clusterGet.count -ne " 0" ) {
Write-Host - foregroundcolor green " [$clusterName ] The cluster exists, we're getting it now."
$clusterID = $clusterGet.results.id
Write-host " [$ ( $vm.name ) ] Looking for a ip matching $ipconcat ."
try { return ((Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/?q=$ipconcat " - Headers $headers - Method Get).content | convertfrom-json ).results[0 ].id }
catch {
Write-host " [$ ( $vm.name ) ] $ipconcat : no match found."
return $null
}
}
function Get-NetboxIPAMPrefixes {
Write-host - ForegroundColor Cyan " Getting Netbox IP prefixes"
return ((Invoke-WebRequest - Uri " $uri /ipam/prefixes/" - Method Get - Headers $headers ).content | convertfrom-json ).results
}
else {
Write-Host - foregroundcolor yellow " [$clusterName ] The cluster doesn't exist. Let's add it !"
$clusterId = ((Invoke-WebRequest - Uri " $uri /virtualization/clusters/" - Method Post - Body $ (@ {name = $clusterName ;type = $NetboxHyperVClusterType }| Convertto-json ) - Headers $headers ).content | convertfrom-json ).id
function Get-NetboxNetworkInterface {
[CmdletBinding ()]
param (
[Parameter ()][Microsoft.HyperV.PowerShell.VMNetworkAdapter ]$networkAdapter ,
[Parameter ()][String ]$virtualMachineId
)
Write-host " [$ ( $vm.name ) ] Looking for a network interface matching VM with id $virtualMachineId ."
try { return (((Invoke-WebRequest - uri " $uri /virtualization/interfaces/?virtual_machine_id=${virtualMachineID} " - Method Get - Headers $headers ).content | convertfrom-json ).results | Where-Object {$_.name -eq $networkAdapter.name }).id }
catch {
Write-host " [$ ( $vm.name ) ] No existing interfaces found that match."
return $null
}
}
function Get-HyperVClusterName {
# If we're not in a cluster, we use "Standalone - $Hostname" as a cluster name instead.
# We try to see if the cluster exists, if it does, we take it's ID for later. If not, we create it.
Write-host - foregroundcolor cyan " [$hyperVHost ] Trying to determine if server is part of a cluster..."
$clusterName = (Get-Cluster - Domain $ ($env: USERDNSDOMAIN ) | Get-ClusterNode | where-object {$_.Name -eq $hyperVHost }).Cluster.name
Write-Host - foregroundcolor cyan " [$clusterName ] Getting the list of VM from Hyper-V." # This script needs to be run as admin on the local server.
$vms = Get-VM
if ($null -eq $clusterName ) {
Write-Host - foregroundcolor cyan " [$hyperVHost ] Host is standalone"
$clusterName = " Standalone-$hyperVHost "
}
foreach ($vm in $vms ) {
return $clusterName
}
function Get-MaskFromPrefix {
[CmdletBinding ()]
param (
[Parameter ()][String ]$prefix
)
$MaskLength = $prefix.split (' /' )[1 ]
[System.Net.IPAddress ] $mask = 0
$mask.Address = ([UInt32 ]::MaxValue) -shl (32 - $MaskLength ) -shr (32 - $MaskLength )
# Write-host "[$($vm.name)] Mask for network prefix $($prefix.split('/')[1]) is $mask."
return $mask
}
function Get-NetboxPrefixFromIP {
[CmdletBinding ()]
param (
[Parameter ()][System.Net.IPAddress ]$ip
)
Write-host " [$ ( $vm.name ) ] Looking for a subnet matching ip $ip ."
foreach ($prefix in $prefixes ) {
$mask = Get-MaskFromPrefix - prefix $prefix.prefix
# Write-host "$($prefix.prefix) has $mask"
if (Test-IPInSubnet - IPAddress $ip - Subnet $prefix.prefix.split (' /' )[0 ] - SubnetMask $mask ) {
write-host - ForegroundColor Green " [$ ( $vm.name ) ] Match found : $ip matches subnet $ ( $prefix.prefix.split (' /' )[0 ]) "
return $prefix.prefix.split (' /' )[1 ]
}
}
return $null
}
function Get-HyperVVMNics {
[CmdletBinding ()]
param (
[Parameter ()][String ]$vmname
)
Write-host " [$ ( $vm.name ) ] Getting network interface list."
return Get-VMNetworkAdapter - VMName $vm.Name - computername $hyperVHost
}
function Set-NetboxCluster {
[CmdletBinding ()]
param (
[Parameter ()][String ]$name
)
Write-host " [$name ] Setting cluster variable."
$id = Get-NetboxCluster ($name )
if ($null -eq $id ) {
Write-host " [$name ] Cluster does not exists - adding."
$id = Add-NetboxCluster ($name )
}
# Resetting these to nothing.
$virtualMachineBody = @ {}
$virtualMachineID = " "
$ip = " "
$ipconcat = " "
$ipID = " "
$interfacePK = " "
return $id
}
function Set-NetboxVMPrimaryIP {
[CmdletBinding ()]
param (
[Parameter ()][String ]$virtualMachineId ,
[Parameter ()][String ]$ipId
)
Write-host " [$ ( $vm.name ) ] Setting primary IP to id $ipId for VM $virtualMachineId "
$result = Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/$virtualMachineId /" - Method Patch - Headers $headers - Body $ (@ {primary_ip4 = $ipId } | ConvertTo-Json )
}
# ########################################
# Script starts here
# ########################################
# NSlookup that bad boy.
Write-Host - ForegroundColor Cyan " [$ ( $vm.name ) ] Looking up IP address"
try {$ip = " $ ( ([System.Net.Dns ]::GetHostAddresses($vm.VMName ).IpAddressToString).ToString()) " } catch {$ip = " " }
# Set API Headers
$headers = New-Object " System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add (" Authorization" , " Token $token " )
$headers.Add (" Content-Type" , ' application/json' )
$headers.Add (" Accept" , ' application/json' )
# Prepare the common variables
$clusterName = Get-HyperVClusterName
$clusterId = Set-NetboxCluster ($clustername )
$prefixes = Get-NetboxIPAMPrefixes
$vms = Get-VM - computername $hyperVHost
if ($vm.state -eq " Running" -and $ip -ne " " ) {
Write-Host - ForegroundColor Cyan " [$ ( $vm.name ) ] VM is running"
$ipconcat = $ip + " /" + " 24"
foreach ($vm in $vms ) {
$virtualMachineId = Add-NetboxVM - vm $vm - clusterId $clusterId - roleId $NetboxServerRoleID
$virtualMachineIPList = @ ()
Write-Host - ForegroundColor Cyan " [$ ( $vm.name ) ] We check if the IP $ipconcat already exists."
if (((Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/?q=$ip &" - Headers $headers - Method Get).content | convertfrom-json ).count -gt 0 ) {
Write-Host - ForegroundColor green " [$ ( $vm.name ) ] IP $ipconcat already exists."
$ipID = ((Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/?q=$ip " - Headers $headers - Method Get).content | convertfrom-json ).results[0 ].id
}
else {
# We create an ip address object.
Write-Host - ForegroundColor yellow " [$ ( $vm.name ) ] IP $ipconcat does not exist, adding it now."
$ipID = ((Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/" - Method POST - Headers $headers - Body $ (@ {address = $ipconcat } | Convertto-json )).content | convertfrom-json ).id
# Foreach interface, create the IP, create the nic and create the association
foreach ($nic in (Get-HyperVVMNics - vmname $vm.name )) {
$ipList = @ ()
foreach ($ip in $nic.IPAddresses | Where-Object {($null -ne $_ ) -and ($_ -notlike " fe80*" )}) {
$ipList += Add-NetboxIP - ip $ip - mask (Get-NetboxPrefixFromIP - ip $ip )
}
# status=1 => Active
$virtualMachineBody = $ (@ {
name = $ ($vm.VMName )
cluster = $clusterId
status = " active"
role = $NetboxServerRoleID
vcpus = $ ($vm.ProcessorCount )
memory = $ (if ($vm.DynamicMemoryEnabled ){$vm.MemoryMaximum / 1 MB }else {$vm.MemoryStartup / 1 MB })
# primary_ip4=$ipID
disk = $ (((($vm.HardDrives | Get-VHD ).Size) | Measure-Object - Sum).Sum/ 1 GB )
} | ConvertTo-Json )
} else {
Write-Host - ForegroundColor Cyan " [$ ( $vm.name ) ] The VM isn't running. We'll just add it here."
# status=0 => Offline
$virtualMachineBody = $ (@ {
name = $ ($vm.VMName )
cluster = $clusterId
status = " offline"
role = $NetboxServerRoleID
vcpus = $ ($vm.ProcessorCount )
memory = $ (if ($vm.DynamicMemoryEnabled ){$vm.MemoryMaximum / 1 MB }else {$vm.MemoryStartup / 1 MB })
disk = $ (((($vm.HardDrives | Get-VHD ).Size) | Measure-Object - Sum).Sum/ 1 GB )
}| ConvertTo-Json )
$nicId = Add-NetboxNetworkInterface - networkAdapter $nic - virtualMachineId $virtualMachineId
Add-NetboxIPNetworkInterfaceAssociation - networkInterfaceId $nicId - ipList $ipList
$virtualMachineIPList += $ipList
}
if (((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/?q=$ ( $vm.name ) " - Method Get - Headers $headers ).content | convertfrom-json ).count -gt 0 ) {
# We get the first VM that matches.
Write-Host - ForegroundColor green " [$ ( $vm.name ) ] VM already exists in netbox."
$virtualMachineID = ((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/?q=$ ( $vm.name ) " - Method Get - Headers $headers ).content | convertfrom-json ).results.id
}
else {
# We create the virtual Machine object.
Write-Host - ForegroundColor yellow " [$ ( $vm.name ) ] VM does not exist in netbox."
$virtualMachineID = ((Invoke-WebRequest - uri " $uri /virtualization/virtual-machines/" - Method POST - Body $virtualMachineBody - Headers $headers ).content | convertfrom-json ).id
# Set the ip from the first interface as being the primary ipv4
if ($virtualMachineIPList -ne @ ()) {
Set-NetboxVMPrimaryIP - virtualMachineId $virtualMachineId - ipId ($virtualMachineIPList | Where-Object {$null -ne $_ })[0 ]
}
<#
# We check if the VM is running and if the NSLOOKUP worked.
# If it does, we add the IP to Netbox and link it to the VM.
#>
if ($vm.State -eq " Running" -and $ip -ne " " ) {
Write-Host - ForegroundColor cyan " [$ ( $vm.name ) ] VM is running, we'll try to assign the IP $ip to it."
# First we check if the VM already has an interface attached.
if (((Invoke-WebRequest - uri " $uri /virtualization/interfaces/?virtual_machine_id=$virtualMachineID " - Method Get - Headers $headers ).content | convertfrom-json ).count -gt 0 ) {
# We get the first interface that matches.
Write-Host - ForegroundColor green " [$ ( $vm.name ) ] IP $ip is already assigned to vm."
$interfacePK = ((Invoke-WebRequest - uri " $uri /virtualization/interfaces/?virtual_machine_id=$virtualMachineID " - Method Get - Headers $headers ).content | convertfrom-json ).results[0 ].id
}
else {
# We create an interface object.
Write-Host - ForegroundColor yellow " [$ ( $vm.name ) ] Adding interface with ip $ip to VM."
$interfacePK = ((Invoke-WebRequest - Uri " $uri /virtualization/interfaces/" - method Post - Headers $headers - body $ (@ {name = ' ethernet' ;virtual_machine = $virtualMachineID } | Convertto-json )).content | convertfrom-json ).id
}
# We update the IP address.
Write-Host - ForegroundColor yellow " [$ ( $vm.name ) ] Updating IP address $ip to VM."
$endResult = Invoke-WebRequest - Uri " $uri /ipam/ip-addresses/$ipID /" - Method Patch - Headers $headers - Body $ (@ {address = $ipconcat ;interface = $interfacePK }| ConvertTo-Json )
}
}