Skip to content

Instantly share code, notes, and snippets.

@cpi-bcso
Forked from InputObject2/Import-NetboxVM.ps1
Last active September 25, 2024 15:08
Show Gist options
  • Save cpi-bcso/53480313149a15b4e35c5645a38f48d3 to your computer and use it in GitHub Desktop.
Save cpi-bcso/53480313149a15b4e35c5645a38f48d3 to your computer and use it in GitHub Desktop.
# Powershell refuses to connect to the Netbox API on our setup without this.
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[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"
# set cluster and host the same name to get all vms on cluster,
# or set hyperVHost to hostname only, to just migrate the machines
# from one host of the cluster. Keep in mind, that all names are
# UPPERCASE when reading from AD
# -> Keep existing Clusternames in Netbox in Sync (uppercase) to
# not recreate them
$hyperVHost = "hyper-v-host"
$hyperVCluster = "hyper-v-cluster"
$hyperVDomain = "domain.tld" # without leading dot
# These are some ID's in Netbox
# We added a "Hyper-V" (id 3) cluster type. This needs to match yours!
# id 25 for us corresponds to the role "Server".
$NetboxHyperVClusterType = 1
$NetboxServerRoleID = 7
# 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/$mask"
$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/1MB}else{$vm.MemoryStartup/1MB})
disk=$(((($vm.HardDrives | Get-VHD -computername $hyperVHost).Size) | Measure-Object -Sum).Sum/1GB)
} | ConvertTo-Json)
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(-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
)
$ipconcat="$ip" + "/" + "$mask"
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
}
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..."
if ( $hyperVCluster = $hyperVHost )
{
$clusterName = (Get-Cluster -Domain $($env:USERDNSDOMAIN) -Name $hyperVCluster | where-object {$_.Name -eq $hyperVHost}).name + "." + "$hyperVDomain"
}
else {
$clusterName = (Get-Cluster -Domain $($env:USERDNSDOMAIN) -Name $hyperVCluster | Get-ClusterNode | where-object {$_.Name -eq $hyperVHost}).Cluster.name + "." + "$hyperVDomain"
}
if($null -eq $clusterName) {
Write-Host -foregroundcolor cyan "[$hyperVHost.$hyperVDomain] Host is standalone"
$clusterName = "Standalone-$hyperVHost.$hyperVDomain"
}
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)
$maskresult = Convert-Subnetmask -CIDR $MaskLength
$mask = $maskresult.{Mask}
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)
}
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)
}
# added new subnetconversion
# Language : PowerShell 4.0
# Filename : Convert-Subnetmask.ps1
# Autor : BornToBeRoot (https://github.com/BornToBeRoot)
# Description : Convert a subnetmask to CIDR and vise versa
# Repository : https://github.com/BornToBeRoot/PowerShell
# .SYNOPSIS
# Convert a subnetmask to CIDR and vise versa
# .LINK
# https://github.com/BornToBeRoot/PowerShell/blob/master/Documentation/Function/Convert-Subnetmask.README.md
function Convert-Subnetmask
{
[CmdLetBinding(DefaultParameterSetName='CIDR')]
param(
[Parameter(
ParameterSetName='CIDR',
Position=0,
Mandatory=$true,
HelpMessage='CIDR like /24 without "/"')]
[ValidateRange(0,32)]
[Int32]$CIDR,
[Parameter(
ParameterSetName='Mask',
Position=0,
Mandatory=$true,
HelpMessage='Subnetmask like 255.255.255.0')]
[ValidateScript({
if($_ -match "^(254|252|248|240|224|192|128).0.0.0$|^255.(254|252|248|240|224|192|128|0).0.0$|^255.255.(254|252|248|240|224|192|128|0).0$|^255.255.255.(255|254|252|248|240|224|192|128|0)$")
{
return $true
}
else
{
throw "Enter a valid subnetmask (like 255.255.255.0)!"
}
})]
[String]$Mask
)
Begin {
}
Process {
switch($PSCmdlet.ParameterSetName)
{
"CIDR" {
# Make a string of bits (24 to 11111111111111111111111100000000)
$CIDR_Bits = ('1' * $CIDR).PadRight(32, "0")
# Split into groups of 8 bits, convert to Ints, join up into a string
$Octets = $CIDR_Bits -split '(.{8})' -ne ''
$Mask = ($Octets | ForEach-Object -Process {[Convert]::ToInt32($_, 2) }) -join '.'
}
"Mask" {
# Convert the numbers into 8 bit blocks, join them all together, count the 1
$Octets = $Mask.ToString().Split(".") | ForEach-Object -Process {[Convert]::ToString($_, 2)}
$CIDR_Bits = ($Octets -join "").TrimEnd("0")
# Count the "1" (111111111111111111111111 --> /24)
$CIDR = $CIDR_Bits.Length
}
}
[pscustomobject] @{
Mask = $Mask
CIDR = $CIDR
}
}
End {
}
}
#########################################
# Script starts here
#########################################
# 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
foreach($vm in $vms) {
$virtualMachineId = Add-NetboxVM -vm $vm -clusterId $clusterId -roleId $NetboxServerRoleID
$virtualMachineIPList = @()
# 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)
}
$nicId = Add-NetboxNetworkInterface -networkAdapter $nic -virtualMachineId $virtualMachineId
Add-NetboxIPNetworkInterfaceAssociation -networkInterfaceId $nicId -ipList $ipList
$virtualMachineIPList += $ipList
}
# 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]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment