cancel
Showing results for 
Search instead for 
Did you mean: 
Create Post

How to use the IPAM API and get "Free IP Address"

When I talked to you - IPAM users, I've heard many times that one of the typical tasks of IP address management is obtaining available IP address(es) for selected subnet. This is tricky if you use spreadsheets and share it with more people (you never know if the doc is in sync and if you won't create IP address conflict).

You also voted for similar feature - IP Request Form on Twhack. Where one of the scenarios is ask & approve assignment of first available address in subnet.

In this post, I'd like to unveil an IPAM API method that can give you one or more Available IP Address in a subnet.

What we need in order to accomplish this trick:

  1. IPAM 4.0 (could be an Eval)
  2. Latest version of Orion SDK installed on the IPAM server.
  3. Access to Windows PowerShell (could be applied on Python, Perl or VBScript as well)
  4. User and Password into IPAM
  5. Ten minutes of your time 🙂

Install and configure your IPAM

Simple start, install and configure your IPAM. It must contain at least one subnet with IP addresses and their statuses inside:

Install your Orion SDK

Orion SDK will provide API access to IPAM database via secure methods. I don't expect any single problem during install process (link for download). My recommendation is to install as local Administrator.

If you run into any problem, please speak up in this forum on Thwack.

Open your PowerShell window

And now the real fun begins. Run your PowerShell Window and make sure that Orion SDK was successfully registered:

Type this command: Get-PSSnapin | where {$_.Name -eq "SwisSnapin"}) and hit enter key.

The result should looks like this:

pastedImage_9.png

If you didn't get this, simple type following command (will add PowerShell snappin from SDK)

Add-PSSnapin "SwisSnapin"


Now you have to setup connection to your database/IPAM.

Type in following commands and change your $hostname to domain name or IP address of your IPAM, $username to the username you want to use for connection and $password to your password (like $password = "solarwinds"). If you are using Eval of IPAM and running the script from the same machine, keep it as it is below.


$hostname = "localhost"

$username = "admin"

$password = New-Object System.Security.SecureString  

$cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

$swis = Connect-Swis -host $hostname -cred $cred

OK, so we set up the connection, and now we can call an API method and get information about available IP Address. You also need to know the name of the subnet from where you want to get free IP Address. In our example I'll use "DEV" name (see screenshot above). Type in the command below and hit enter key:

Get-SwisData $swis 'SELECT TOP 1 I.Status, I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.DisplayName = ''DEV'''

Et voila:

pastedImage_30.png

As you may see we got first free available IP Address as 10.140.126.4 with status "2" (Available") which corresponds to my sample IPAM data:pastedImage_31.png

OK so that's good, but I don't assume that you'll want to run such background script every time you want to get your first available IP Address. You can certainly save the whole script into a file and then run it just by simple click (feel free to download my example).

The result of the "Get-SwisData" is stored in DataSet - .NET object which you may use for further processing. For example, you can store the results to the file or you can call it from the web service or helpdesk system.

If you want to iterate via IP addresses in the result, you may use this powershell query (useful when getting more than 1 free IP address or you want to run the query for more subnets):


$addresses = Get-SwisData $swis 'SELECT TOP 1 I.Status, I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.DisplayName = ''DEV'''


foreach($address in $addresses)

{

     write-host "Free IP Address is" $address.DisplayName

}

blogpost foreach.png

We can slightly modify SWQL query and populate subnet address and CIDR next to the available IP Address (by the way, this is the beauty of SWQL language, you don't have to use JOINs in many cases, simply use dot notation in order to list properties of related entity - in this case "Subnet"):

$addressesWithSubnets = SELECT R.Address as SubnetAddress, R.CIDR, R.FriendlyName, R.PercentUsed,

(SELECT TOP 1 I2.IpAddress FROM IPAM.IPNode as I2 WHERE I2.Status=2 AND I2.SubnetId = R.GroupID ) AS FreeIpAddress

FROM IPAM.GroupReport as R WHERE R.GroupType='8'

Where GroupTyp=8 means type "subnets" (not supernets or DHCP scopes,etc.)


Then the result may look like this:

subnets-script.png

I can iterate through the $addressesWithSubnet variable and do whatever I need witch each record, for example send email, send data into help desk, update database (custom property) or store it into a file.

The interesting option is create a webservice that can call this IPAM powershell script with attribute "subnet name". Then you can ask for first available IP address from anywhere.

Example where simply writing each row to the console output:

list of subnets.png

Download the full script.

Example of .NET webservice that can call our PowerShell script:


[WebMethod]

//When calling the method put the path to our script file as parameter to the method GetFreeAvailableAddress.

//For example: c:\script\getFreeIP.ps

//Feel free to add exception handling you prefer.


public void GetFreeAvailableAddress(string script)
{
  
RunspaceConfiguration rC = RunspaceConfiguration.Create();

  
Runspace runspace = RunspaceFactory.CreateRunspace(rC);
   runspace
.Open();

  
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);

  
Pipeline pipelineCommand = runspace.CreatePipeline();
   pipelineCommand
.Commands.AddScript(script);

  
// execute the script
   pipelineCommand
.Invoke();
}

Integration with SAM (Server and Application Monitor)

One of the easiest way to get Powershell script monitored by Orion web interface is via SAM (try eval if you don't have it). SAM can monitor powershell scripts and show the results on the web very simply.

I had to just re-format the output of my PowerShell script and then define new PowerShell template in SAM so it can transfer the data from script on web.

There is a very nice article about how to add Script Monitor into SAM. So I'll skip this phase and just summarize that what you need to do on your Script side, is populate data you want to show via two specific messages:

Detail Type

Required

Meaning

Statistic

Yes

A numeric value used to determine how the monitor compares to its set thresholds. This must be an integer value, (negative numbers are supported).

Statistic.Name1: 123

Statistic.Name2: 456

Message

No

An error or information message to be displayed in the monitor status details. Note: Multi-line messages are supported. To use this functionality print each line using a separate command. For example:
Message.Name1: abc

Message.Name2: def

and it must end by "Exit(0)" statement.

I modified our script in order to produce messages that are parsed properly by SAM (how to add add new SAM template). The template show you percentage used of IP address per subnet and also first free IP Address for each subnet.


Section added


Write-Host "Message.$($freeIP.FriendlyName): Subnet: $($freeIP.SubnetAddress)/$($freeIP.CIDR) named: $($freeIP.FriendlyName) has this available IP  ADDRESS$($freeIP.FreeIPAddress)";   

Write-Host "Statistic.$($freeIP.FriendlyName): $($freeIP.PercentUsed)";

Feel free to download SAM template from here. Wondering how the result looks like on the web? Here it goes:

utilization.png

statistics comments.png

Let me know if you have any questions and don't be intimidated by our API, it's very easy to use.

21 Comments
Level 16

Recent versions of the SDK have a simpler option for passing hard-coded credentials to Connect-Swis. Instead of this:

$hostname = "localhost"

$username = "admin"

$password = New-Object System.Security.SecureString  

$cred = New-Object -typename System.Management.Automation.PSCredential -argumentlist $username, $password

$swis = Connect-Swis -host $hostname -cred $cred

You can now do this:

$hostname = "localhost"

$username = "admin"

$password = ""

$swis = Connect-Swis –host $hostname –username $username –password $password

Even better way how to use SWQL is use parameters in the query:

Get-SwisData $swis 'SELECT TOP 1 I.Status, I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.DisplayName = @subnet' @{subnet="DEV"}

Level 12

Thanks for this great post!  I'm pretty excited to start using this, however, I get the following message when I try follow your instructions (using the updated credentials code):

Get-SwisData : Invalid username or password.

At line:1 char:13

+ Get-SwisData <<<<  $swis 'SELECT TOP 1 I.Status, I.DisplayName FROM IPAM.IPNode I WHERE Status=2 AND I.Subnet.Display

Name = @subnet' @{subnet="10.25.0.0/16"}

    + CategoryInfo          : InvalidOperation: (:) [Get-SwisData], MessageSecurityException

    + FullyQualifiedErrorId : SwisError,SwisPowerShell.GetSwisData

Since I never get prompted for the password, I am pretty sure that it doesn't like the password, but how do I send it the password??

Thanks again!

Level 16

Hi Randy,

you need to set password variable from above. example: $password = "OrionAPI";

let me know if that helps.

Michal

Level 16

Are there any API operations to

a) add a subnet

b) set its properties (e.g. neighbor for discovery)

and having found a free IP address, are there any API verbs that allow you to set it its properties (for example that it is now in use, so the next person trying to find a free IP address gets a different one)?

Thanks

Level 12

Michal,

I did try that but I still get the "Invalid username or password" message.  I have tried using AD credentials for users that have access to Orion/IPAM, local user accounts within Orion, and finally as a last resort, the built-in Admin account in the Orion system.  All result in the same error message.  What am I missing?

Thanks!
-Randy

Level 8

IMHO, Without the ability to modify the status of an IPAM.IPNode to mark it as InUse/Reserved after an IP is returned as Available, publishing the "Next Available IP" via an API can introduce more issues than this simple query might resolve if the query is run in any kind of environment where multiple systems are being provisioned on a single subnet at once.

Level 7

I completely agree, its really only half of the equation .... We currently use IPAM and its a great tool ..saved plenty of time by giving our system engineers small range of ip's to work with and not be bothered for an IP address everyday......however, we are moving to create our own private "cloud" services. Our team who handles our UCS environment recently came to me regarding "automation". They having scripted builds,that puts the O/S,then depending on the function of the vm's they are automatically placed in the right vlan with port profiles on the 1000v switch ect...scripting out to grab an ip and changing the status/dns entries is what they need and looking for to make it even more "cloud" friendly. Of course not everything will be on autopilot , there's always going to be manual builds and we still have a big need to be using the gui .  We will be there soon, so I'm hoping IPAM will provide full scripted functions to use with the API.

Level 8

Thanks for your feedback NetWar. That's Exactly the situation I'm in. I have to provision lots of systems each day and, so far, most of the process except IP allocation/StatusUpdate is handled with automation; However, I'm running into blocker (race conditions and Catch22) issues where the automation cannot proceed until the ability to reserve IP's in IPAM can be handled by the automation (PowerShell SnapIn, or even C# API's).

Please add a vote to the existing Feature Request to "ADD SWIS FEATURES TO IPAM": http://thwack.solarwinds.com/ideas/1655 (AKA: CRUD for IPAM objects via the SDK).

Level 8

So, while we wait for SDK CRUD against IPAM objects (Vote Herehttp://thwack.solarwinds.com/ideas/1655 ), I'll take this opportunity to share with the community some Powershell I wrote against the IPAM-API:

I had to write a bunch of this kind of functionality last year for my IPAM install to allow provisioning of multiple IP's at once on the same subnet, here's some of the Powershell functions that i turned into a Powershell Module file (psm1).

If you have any questions, please ask; I'll be happy to add as much info as I can to make this as useful as possible.

Once IPAM CRUD happens, i'll be updating my modules and if people are interested, i'll be happy to post them here.

Example of how to use this module:

#ExampleCode Start--------------------------------------------------------------------------------

Add-PSSnapin SwisSnapin;

import-module .\IPAM.psm1;

$SWISServerName = "10.10.1.155";

$DomainCredential = get-credential;

$Global:SWIS = Connect-Swis -Hostname $SWISServerName -cred $DomainCredential; #Storing credentials in clear-text in a script is ALWAYS bad practice, highly suggest using the PSCredential Object's ability to be written to a file and encrypted with a SSLCertificate for a much more secure way to store credentials for scripts to use.

#Alternatively, if you install the SWIS certificate from the IPAM server on a specific server (where your scripts will run from), you dont need to provide credentials to connect: $Global:SWIS = Connect-Swis -Hostname $SWISServerName -Certificate;

$Global:UID = "ServerProvisioning";

$script:IPSubnet="10.100.123.0"

Try{

  $Script:VMIPAddress=(IPAM-GetFirstFreeInSubnet $Script:IPSubnet -vb:$vbt);

  IPAM-ReserveIP -IP $VMIPAddress -Description "$($VMIPAddress)_$($CustomerName)_$($ServerRoleName)_$($ServerEnvironment)_$(get-date -format "yyyyMMddHHmmssms")" -vb:$vbt;

  LogWrite "Runtime ($($sw.elapsed));$(get-date): VMIPAddress($VMIPAddress)";

  $Script:VMIPSubnetMask=IPAM-GetIPSubnetInfo $Script:VMIPAddress -vb:$vbt; LogWrite "Runtime ($($sw.elapsed));$(get-date): VMIPSubnetMask($VMIPSubnetMask)";

  $Script:VMIPGateway=IPAM-GetSubnetDefaultGateay $Script:VMIPAddress -vb:$vbt;LogWrite "Runtime ($($sw.elapsed));$(get-date): VMIPGateway($VMIPGateway)";

  $Script:VMIPDNSOrder=IPAM-GetSubnetDNSList $Script:VMIPAddress -vb:$vbt;LogWrite "Runtime ($($sw.elapsed));$(get-date): VMIPDNSOrder($VMIPDNSOrder)";

  $Script:VMRoutes=IPAM-GetSubnetStaticRoutes $Script:VMIPAddress -vb:$vbt;LogWrite "Runtime ($($sw.elapsed));$(get-date): VMRoutes($VMRoutes)";

  }catch{$errstr="Runtime ($($sw.elapsed));$(get-date): Error accessing IPAM($IPSubnet) ($_). Quitting."; LogWrite $errstr; EventLogERROR -vb:$vbt -UID $UID -Message "$errstr`n`n`n$(Get-PSCallStack | format-list * -force | Out-string)"; Throw $errstr;  }

#ExampleCode End--------------------------------------------------------------------------------

#IPAM.psm1 Start-----------------------------------------------------------------------------

Function IPAM-ConvertIP2HexGUID{

[CmdletBinding()]

Param([string]$IPAddress);

  $ret=([Convert]::ToString(([int](($IPAddress.Split("."))[3])),16)).PadLeft(2,"0")+([Convert]::ToString(([int](($IPAddress.Split("."))[2])),16)).PadLeft(2,"0")+([Convert]::ToString(([int](($IPAddress.Split("."))[1])),16)).PadLeft(2,"0")+([Convert]::ToString(([int](($IPAddress.Split("."))[0])),16)).PadLeft(2,"0")+"-0000-0000-0000-000000000000";

  write-verbose "IPAM-ConvertIP2HexGUID($IPAddress)=($Ret)";

  return $ret;

}Export-ModuleMember -Function IPAM-ConvertIP2HexGUID

Function IPAM-GetUnusedIPbySubnet{

[CmdletBinding()]

Param([Parameter(Mandatory=$true)][string]$IPSubnet,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  IPAM-CheckLocalReservedAgainstSWIS;

  $List=@();

  $GroupID = (Get-SwisData $SwisConnection "SELECT GroupId, Address FROM IPAM.IPHistorySubnetInfo WHERE (Address='$IPSubnet')").GroupID;

  $qry="SELECT SubnetID, IPOrdinal, IPAddress, IPAddressN, Alias, DnsBackward, Description, Comments, Status FROM IPAM.IPNode WHERE (Status = 2) AND (Comments IS NULL) AND (Alias IS NULL) AND (DNSBackward IS NULL) AND (SubnetID=$GroupID) ORDER BY IPOrdinal ASC"

  $AvailableIPs = Get-SwisData $SwisConnection $qry;

  if($AvailableIPs -ne $null){

  write-verbose "IPAM-GetUnusedIPbySubnet($IPSubnet): Successful Query ($qry)";

  $LocalReserved = (IPAM-GetLocalReserved);

  foreach($ip in $AvailableIPs){

  $available=$true;

  if($LocalReserved -ne $null){foreach($ReservedIP in $LocalReserved){if($ReservedIP.Contains($IP.IPAddress)){$Available=$false;}}}

  if($available){$list+=$IP.ipaddress;}

  }

  write-verbose "IPAM-GetUnusedIPbySubnet($IPSubnet): $($list.length)";

  }

  return $list;

};Export-ModuleMember -Function IPAM-GetUnusedIPbySubnet;

Function IPAM-GetFirstFreeInSubnet{

[CmdletBinding()]

Param([Parameter(Mandatory=$true)][string]$IPSubnet,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  $IP=IPAM-GetUnusedIPbySubnet $IPSubnet | select -first 1 -ea 0;

  write-verbose "IPAM-GetFirstFreeInSubnet($IPSubnet) = ($IP)";

  return $IP;

};Export-ModuleMember -Function IPAM-GetFirstFreeInSubnet;

Function IPAM-GetLastFreeInSubnet{

[CmdletBinding()]

Param([Parameter(Mandatory=$true)][string]$IPSubnet,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  $IP=IPAM-GetUnusedIPbySubnet $IPSubnet | select -last 1 -ea 0;

  write-verbose "IPAM-GetFirstFreeInSubnet($IPSubnet) = ($IP)";

  return $IP;

};Export-ModuleMember -Function IPAM-GetLastFreeInSubnet;

Function IPAM-GetUnusedIPByRanges{

[CmdletBinding()]

Param([Parameter(Mandatory=$true)][string]$IPRanges,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  write-verbose "IPAM-GetUnusedIPByRanges($IPRanges)";

  <#

  $IPRanges="192.168.1.1-192.168.1.254;192.168.2.1-192.168.2.254"

  #>

  IPAM-CheckLocalReservedAgainstSWIS;

  $QueryRanges=@();

  $List=@();

  $Ranges=$IPRanges.Split(";");

  ForEach($Range in $Ranges){

  if($Range.length -ge 15){

  $LowerRange=(IPAM-ConvertIP2HexGUID ($Range.Split("-"))[0]);

  $UpperRange=(IPAM-ConvertIP2HexGUID ($Range.Split("-"))[1]);

  $QueryRanges +="((IPAddressN >= '$LowerRange') AND (IPAddressN <= '$UpperRange'))";

  if($QueryRanges.Length -gt 1){$QueryRanges[$QueryRanges.Length-1]=" OR "+$QueryRanges[$QueryRanges.Length-1];}

  }

  }

  $QueryString="";

  ForEach($range in $QueryRanges){$QueryString=$QueryString+$Range}

  $qry="SELECT IPOrdinal, IPAddress, IPAddressN, Alias, DnsBackward, Description, Comments, Status FROM IPAM.IPNode WHERE (Status = 2) AND (Comments IS NULL) AND (Alias IS NULL) AND (DNSBackward IS NULL) AND ($QueryString) ORDER BY IPOrdinal ASC"

  #write-host $qry;

  $AvailableIPs = Get-SwisData $SwisConnection $qry;

  $LocalReserved = (IPAM-GetLocalReserved);

  foreach($ip in $AvailableIPs){

  $available=$true;

  if($LocalReserved -ne $null){foreach($ReservedIP in $LocalReserved){if($ReservedIP.Contains($IP.IPAddress)){$Available=$false;}}}

  if($available){$list+=$IP.ipaddress;}

  }

  return $list;

};Export-ModuleMember -Function IPAM-GetUnusedIPByRanges;

Function IPAM-GetUnusedIPs{

[CmdletBinding()]

Param([Parameter(Mandatory=$true)][string]$IPSpace,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  write-verbose "IPAM-GetUnusedIPs($IPSpace)";

  IPAM-CheckLocalReservedAgainstSWIS;

  $List=@();

  $AvailableIPs = Get-SwisData $SwisConnection "SELECT SubnetID, Alias, DNSBackward, Comments, IPAddress, Status FROM IPAM.IPNode WHERE ((IPAddress LIKE '$($IPSpace)%') AND (Alias IS NULL) AND (DnsBackward IS NULL) AND (Comments IS NULL) AND (Status = 2)) ORDER BY IPAddress DESC";

  #$AvailableIPs = Get-SwisData $SwisConnection "SELECT IpNodeId, SubnetId, IPOrdinal, IPAddress, IPAddressN, IPMapped, IPMappedN, Alias, MAC, DnsBackward, DhcpClientName, sysName, Description, Contact, Location, SysObjectID, Vendor, VendorIcon, MachineType, Comments, ResponseTime, LastBoot, LastSync, LastCredential, Status, AllocPolicy, SkipScan, LeaseExpires, DnsBy, MacBy, StatusBy, SystemDataBy FROM IPAM.IPNode WHERE (IPAddress = '192.168.174.27')";

  <#$AvailableIPs type [PscxDynamicType40] #>

  $LocalReserved = (IPAM-GetLocalReserved);

  foreach($ip in $AvailableIPs){

  $available=$true;

  if($LocalReserved -ne $null){foreach($ReservedIP in $LocalReserved){if($ReservedIP.Contains($IP.IPAddress)){$Available=$false;}}}

  if($available){$list+=$IP.ipaddress;}

  }

  $list;

  <# Returns an array of strings  (GetIPAM-UnusedIPs $SubnetName)[0]  #>

};Export-ModuleMember -Function IPAM-GetUnusedIPs;

Function IPAM-ReserveIP(){

  [CmdletBinding()]

  Param(

  [Parameter(Mandatory=$true)][string]$IP,

  [Parameter(Mandatory=$true)][string]$Description,

  [string]$RFCID=$Global:UID,

  [switch]$return=$false

  );

  $unavailable=$false;

  $null=IPAM-GetLocalReserved | foreach{if($_ -ne $null){if($_.contains($IP)){$Unavailable=$true}}}

  if($unavailable){

  write-warning "$($IP): IP Is already Reserved"; Return $false;

  }

  write-verbose "IPAM-ReserveIP($IP , $Description , $UID)";

  if(-not (test-path "HKLM:\Software\PSAutomation\$UID")){$null=new-item -path "HKLM:\Software\PSAutomation\$UID" -force}

  $s=(set-itemproperty  "HKLM:\Software\PSAutomation\$UID" -name "IPAM-ReserveIP_$($IP)($($Description.Replace(' ','')))" -value ([string]$IP) -ea 0);

  if($?){if($return){Return $true;}}else{if($return){Return $false;}}

};Export-ModuleMember -Function IPAM-ReserveIP;

Function IPAM-GetLocalReserved{

[CmdletBinding()]Param([string]$UID=$Global:UID);

  $ret=(get-itemproperty -path "hklm:\software\PSAutomation\$UID").psobject.properties | where{$_.name.contains("ReserveIP")} | foreach{$_.value;}

  write-verbose "IPAM-GetLocalReserved($UID) ($($ret -join ','))";

  return $ret;

};Export-ModuleMember -Function IPAM-GetLocalReserved;

Function IPAM-ListLocalReserved{

[CmdletBinding()]Param([string]$UID=$Global:UID);

  write-verbose "IPAM-ListLocalReserved($UID)";

  return (get-itemproperty -path "hklm:\software\PSAutomation\$UID").psobject.properties | where{$_.name.contains("ReserveIP")} | foreach{$_.name}

};Export-ModuleMember -Function IPAM-ListLocalReserved;

Function IPAM-RemoveLocalReserved{

[CmdletBinding()]Param($IP, [string]$UID=$Global:UID, [switch]$return=$false);

  write-verbose "IPAM-RemoveLocalReserved($IP, $UID)";

  if($IP -eq $null){write-warning "IPAM-RemoveLocalReserved: IP cannot be Null. Break.";return $null}

  $ret=$false;

  $regpath="hklm:\software\PSAutomation\$UID";

  if(-not (test-path $regpath)){throw "Registry path Not Found ($regpath)."}

  foreach($I in $IP){

  $t=(get-itemproperty -path $regpath).psobject.properties | where{([string]($_.value)).contains($I)}

  if($t -ne $null){

  $r=remove-itemproperty -path $regpath -name $t.name -ea 0;

  if($r -ne $null){write-verbose "IPAM-RemoveLocalReserved: IP Removed from Local ($($r.name))";$ret=$true;}

  else{$ret=$true}

  }

  }

  if($return){return $ret}

};Export-ModuleMember -Function IPAM-RemoveLocalReserved;

Function IPAM-ReserveFirstNIPsInNetwork(){

[CmdletBinding()]

  Param(

  [Parameter(Mandatory=$true)][int]$n,

  [Parameter(Mandatory=$true)][string]$octet1,

  [Parameter(Mandatory=$true)][string]$octet2,

  [Parameter(Mandatory=$true)][string]$octet3,

  [Parameter(Mandatory=$true)][string]$Description

  );

  write-verbose "IPAM-ReserveFirstNIPsInNetwork($n, $octet1, $octet2, $octet3, $Description)";

  $f=IPAM-GetUnusedIPs "$($octet1).$($octet2).$($octet3).";

  if($f -ne $null){

  if($f.length -ge $n){

  for($i=0;$i -lt $n;$i++){

  IPAM-ReserveIP -IP ($f[$i]) -Description $Description;

  }

  }

  }else{return $null}

};Export-ModuleMember -Function IPAM-ReserveFirstNIPsInNetwork;

Function IPAM-CheckLocalReservedAgainstSWIS{

[CmdletBinding()]Param([SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  $LocalReserved = IPAM-GetLocalReserved;

  if($LocalReserved -ne $null){

  foreach($IP in $LocalReserved){

  $a = Get-SwisData $SwisConnection "SELECT IPAddress, Status FROM IPAM.IPNode WHERE (IPAddress = '$($IP)')";

  if($a -ne $null){

  write-verbose "IPAM-CheckLocalReservedAgainstSWIS($($a.IPAddress))($($a.status))";

  if($a.Status -ne 2){

  write-verbose "IPAM-CheckLocalReservedAgainstSWIS: LocalReservedIP($IP) reserved in SWIS: RemoveLocalReserved($IP)";

  IPAM-RemoveLocalReserved $IP;

  }}

  }

  }

}; Export-ModuleMember -Function IPAM-CheckLocalReservedAgainstSWIS;

Function IPAM-GetIPSubnetInfo{

[CmdletBinding()]Param([string]$IP,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  $IPSubnetID = (Get-SwisData $SwisConnection "SELECT SubnetId,IPAddress FROM IPAM.IPNode WHERE (IPAddress='$IP')").SubnetID;

  $SNM = (Get-SwisData $SwisConnection "SELECT GroupID,Address,CIDR FROM IPAM.IPHistorySubnetInfo WHERE (GroupID=$IPSubnetID)");

  write-verbose "IPAM-GetIPSubnetInfo($IP): IPSubnetID($IPSubnetID) SubnetMask($($SNM.CIDR))";

  return ConvertTo-SubnetMask $SNM.CIDR

};Export-ModuleMember -Function IPAM-GetIPSubnetInfo;

Function IPAM-GetSubnetDefaultGateay{

[CmdletBinding()]Param([string]$IP,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  <# Requires custom fields added to each subnet which contains this information, allows all networking info to be sourced from IPAM queries #>

  $IPSubnetID = (Get-SwisData $SwisConnection "SELECT SubnetId,IPAddress FROM IPAM.IPNode WHERE (IPAddress='$IP')").SubnetID;

  $DFG = (Get-SwisData $SwisConnection "SELECT GroupId, DefaultGateway FROM IPAM.GroupNodeAttr WHERE (GroupID=$IPSubnetID)").DefaultGateway;

  write-verbose "IPAM-GetSubnetDefaultGateay($IP): IPSubnetID($IPSubnetID) DefaultGateway($DFG)";

  return $DFG;

};Export-ModuleMember -Function IPAM-GetSubnetDefaultGateay;

Function IPAM-GetSubnetDNSList{

[CmdletBinding()]Param([string]$IP,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  <# Requires custom fields added to each subnet which contains this information, allows all networking info to be sourced from IPAM queries #>

  $IPSubnetID = (Get-SwisData $SwisConnection "SELECT SubnetId,IPAddress FROM IPAM.IPNode WHERE (IPAddress='$IP')").SubnetID;

  $DNS=(Get-SwisData $SwisConnection "SELECT GroupId, DNSList FROM IPAM.GroupNodeAttr WHERE (GroupID=$IPSubnetID)").DNSList;

  write-verbose "IPAM-GetSubnetDNSList($IP): IPSubnetID($IPSubnetID) DNSList($DNS)";

  return $DNS;

};Export-ModuleMember -Function IPAM-GetSubnetDNSList;

Function IPAM-GetSubnetStaticRoutes{

[CmdletBinding()]Param([string]$IP,[SolarWinds.InformationService.Contract2.InfoServiceProxy]$SwisConnection=$Global:SWIS);

  <# Requires custom fields added to each subnet which contains this information, allows all networking info to be sourced from IPAM queries #>

  $IPSubnetID = (Get-SwisData $SwisConnection "SELECT SubnetId,IPAddress FROM IPAM.IPNode WHERE (IPAddress='$IP')").SubnetID;

  $StaticRoutes=(Get-SwisData $SwisConnection "SELECT GroupId, StaticRoutes FROM IPAM.GroupNodeAttr WHERE (GroupID=$IPSubnetID)").StaticRoutes;

  write-verbose "IPAM-GetSubnetStaticRoutes($IP): IPSubnetID($IPSubnetID) StaticRoutes($StaticRoutes)";

  return $StaticRoutes

};Export-ModuleMember -Function IPAM-GetSubnetStaticRoutes;

Function ConvertTo-SubnetMask {

  <#

    .Synopsis

      Returns a dotted decimal subnet mask from a mask length.

    .Description

      ConvertTo-SubnetMask returns a subnet mask in dotted decimal format from an integer value ranging

      between 0 and 32. ConvertTo-SubnetMask first creates a binary string from the length, converts

      that to an unsigned 32-bit integer then calls ConvertTo-DottedDecimalIP to complete the operation.

    .Parameter MaskLength

      The number of bits which must be masked.

  #>

  [CmdLetBinding()]

  Param(

    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]

    [Alias("Length")]

    [ValidateRange(0, 32)]

    $MaskLength

  )

  Process {

  $SNM=ConvertTo-DottedDecimalIP ([Convert]::ToUInt32($(("1" * $MaskLength).PadRight(32, "0")), 2))

  write-verbose "ConvertTo-SubnetMask($MaskLength)= ($SNM)";

    Return $SNM;

  }

};Export-ModuleMember -Function ConvertTo-SubnetMask;

Function ConvertTo-DottedDecimalIP {

  <#

    .Synopsis

      Returns a dotted decimal IP address from either an unsigned 32-bit integer or a dotted binary string.

    .Description

      ConvertTo-DottedDecimalIP uses a regular expression match on the input string to convert to an IP address.

    .Parameter IPAddress

      A string representation of an IP address from either UInt32 or dotted binary.

  #>

  [CmdLetBinding()]

  Param(

    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]

    [String]$IPAddress

  )

  Process {

    Switch -RegEx ($IPAddress) {

      "([01]{8}\.){3}[01]{8}" {

        $ret= [String]::Join('.', $( $IPAddress.Split('.') | ForEach-Object { [Convert]::ToUInt32($_, 2) } ))

      }

      "\d" {

        $dIPAddress = [UInt32]$IPAddress;

        $DottedIP = $( For ($i = 3; $i -gt -1; $i--) {

          $Remainder = $dIPAddress % [Math]::Pow(256, $i)

          ($dIPAddress - $Remainder) / [Math]::Pow(256, $i)

          $dIPAddress = $Remainder

         } )

        $Ret=[String]::Join('.', $DottedIP);

      }

      default {

        Write-Error "ConvertTo-DottedDecimalIP($IPAddress): Cannot convert this format"

      }

    }

  write-verbose "ConvertTo-DottedDecimalIP($IPAddress) = ($ret)";

  return $ret;

  }

}

#IPAM.psm1 End-----------------------------------------------------------------------------

Level 8

We are Solarwinds IPAM customers and we love the product.  It is super stable and has heaps of functionality.

About a year ago, my current project said, "as part of our data center automation we will have an orchestrator that will ask questions such as 'Please give me the next available IP address in the following security zone.  This IP address will be used as a load balancer VIP and the syatem that will be assigned to it is xxx.www.xxx.zzz.  Oh and by the way my ID is Tommy, my business unit is XXX, we want the VM located in Kalamazoo data center, the application that it is part of is called ATOMIC, the tracking incident number in the incident management system is I012999-08-ABC-42, and lots of other stuff"

Now - no system out of the box is going to do all that for you.   but solarwinds IPAM has a great database and it has CUSTOM fields!  bingo.

so i added fields to the d/b such as incident number,, requester id, security zone etc.

at this stage there was no IPAM API so i decided to experiment with using Python to read and write directly to the IPAM database using MSSQL.  It works great!  so a few (hundred) lines of python code later, using tools such as CherryPy we now have exposed Solarwinds IPAM as a websrivice.  The orchestrator asks the webservice for an address with all the required criteria.  We reserve the next available address and send back the result to the orchestrator.

The cool thing is that IPAM scans the IP subnets automatically.  So when the VM that has been requested comes on line the status changes from RESERVED to USED.  i.e service activation AND service assurance!  Also, my webservice and IPAM both write history records to the IPAM history database - so we can see exactly what happened when for any IP address!

So, I have added lots more functions to the RESTful webservice that make it more and more useful from an automation point of view.  I added a class today that lets users query the database based on a "namefragment".  i.e. "give me a list of all the servers in IPAM that have the characters "web" in their server name".  This is a quick way to find all the webservers we have.  for us anyway.

i also have written RESTful services for such things as "tell me everything you know about 192.168.15.8/32" or "tell me everything you know about 192.168.15.0/24" "tell me everything you know about 192.168.15.23-192.168.15.107".  the consumer gets back everything, vlan id, default gateway, security zone etc etc etc.

i have done this by listening to the questions i get asked (over and over again) and then working out how i can answer these using the IPAM data we have.

it's fun!

Happy to help or answer any questions.

Joel.

Level 7

I was in the same situation as everyone on this thread.  Basically we were unable to set the status of the next available IP address so that no other system uses the IP before the next IP scan.  Here is the process I used to get around this.

1. Use a REST call based on the code at the beginning of this thread to get the next available IP.

2. I then switched to the Orion interface and accessed the site directly mimicking a browser session.

First login to Orion

I understand you can do this with Basic Auth too but this step uses forms for an example.

Using cURL
curl -c orion_cookies.txt --data "__EVENTTARGET=ctl00%24BodyContent%24ctl05&__EVENTARGUMENT=&ctl00%24BodyContent%24Username=user123&ctl00%24BodyContent%24Password=Secret" http://myserver:8080/Orion/Login.aspx?autologin=no  -v
Using Powershell
$postParams = @{__EVENTTARGET='ctl00$BodyContent$ctl05’;__EVENTARGUMENT='';'ctl00$BodyContent$Username'=user123;'ctl00$BodyContent$Password'=‘Secret’}
Invoke-WebRequest -Uri http://myserver:8080/Orion/Login.aspx?autologin=no -Method POST -SessionVariable my_session -Body $postParams -OutFile output.html -MaximumRedirection 0

Then set the IP address to status of Used

Using cURL

curl -b orion_cookies.txt --data "entity=IPAM.IPNode&verb=UpdateManyStatus&ObjectId=1009&ipstatus=1&ids=20" http://myserver:8080/Orion/IPAM/ExtCmdProvider.ashx  -v

Using Powershell

$postParams = @{‘entity’=’IPAM.IPNode’;’verb’=’UpdateManyStatus’;’ObjectId’=’1009 ’;’ipstatus’=’1 ’;’ids’=’20 ’}

Invoke-WebRequest -Uri http://myserver:8080/Orion/IPAM/ExtCmdProvider.ashx  -Method POST -SessionV

ariable my_session -Body $postParams -OutFile output.html -MaximumRedirection 0

There are 3 things you need to complete this:

1. ObjectID:  This a unique key used for each subnet in Orion.  You can get this information by polling Orion with something similar to this:

SELECT S.VLAN, S.SubnetID, S.DisplayName FROM IPAM.Subnet S

2. ipstatus:  This is simple.  1 = Used, 2 = Avaliable

3. ids: This is the nth ip address the subnet (for lack of knowing a better word for it).

Example for a 192.168.208.0/21 subnet

IP=192.168.208.17 would mean an ids=17

This will set the status of that IP address to "Used".  This keeps the IP address away from our provisioning processes until the next day when the whole subnet gets scanned by IPAM.

Level 8

matingara,

I'm needing to set up solarwinds as our automated IPAM tool with vCAC/vCO. I've done this with infoblox but they had a pre-built plugin with easily modified workflows. Are you familiar with how to do this with vCO and the rest plugin? If you could give me some tips I would really appreciate it. I need to scan a subnet, select the next available IP, and claim it with the device name.

thanks!

Level 8

Did you ever get this resolved? I'm having the exact same issue. I've tried:

$password = New-Object System.Security.SecureString

$password = "password"

$password = "passwordhere" | ConvertTo-SecureString -asPlainText -Force

No matter what, when I do a "get-swisdata $swis ..." I get "get-swisdata: invalid username or password."

Level 8

Hey, are you still around? Whenever I do this, I do it exactly as you have it, but when I try the 2nd part, my output.html shows the solarwinds login page. The first part works as the output.html shows the main page AFTER you login, but if I immediately run the 2nd part of the command, it's as if I was never logged in to begin with.

Any ideas?

Edit: I figured this part out. You say to use "-sessionvariable" in the second part but that is incorrect. You have to use "-websession" to call the -sessionvarible you originally set. Now I'm just trying to see if hostname is a possible entry along with IP and used.

Level 7

matingara,

I'm pretty much in the same boat as chrisheartland. I'm using vCAC/vCO  & leveraging puppet for configuration but still running into issues automating IPAM , so any tips,pointers or script\apps would be awesome 

Level 8

vmguy, the best I've come up with, which was detailed by another user, is to script the IPAM record as "used" but that's it. You cannot set the hostname based on the limitation of the api. The recourse is to then have solarwinds scan the subnet for objects and in theory the server name will then populate as the host name based on DNS.

I'm not really a solarwinds user (just using it for IPAM) so I'm not sure how to set up/schedule the scan.

Level 7

matingara

Hey Joel,

Do you have any documentation as to what tables need to be updated when you programmatically mark an IPNode as Used?

I am trying to automate by using System Center Orchestrator and make direct edits to the database.  I have no problem updating the IPNode information, but it does not alter the numbers related to number of available IP addresses per subnet.

Is there a stored procedure that needs to be run and if so can you assist in what the information needs to be passed to the sp to update further tables.

Any help is appreciated.

Hank

Level 7

Any interest in sharing the code you developed?

Level 12

If you are authenticating through AD remember to use the format domain\username for username I've been using...

$swis = Connect-Swis –Trusted –Hostname localhost   <<----- this works ok for me

Level 7

Hi All

I've been snooping around the DB and looking at what changes when you reserve an IP Address.  Here are three sql statements that I thought were relevant. 

The first, updates the actual IP Address and marks it reserved.  @set=4 is the code for reserved.  @SubnetID=100 is the id for the subnet and b@byManual=8192 is the user that made the change.  so you will need to make the relevant changes.

exec sp_executesql N'UPDATE IPAM_Node SET Status = @set , StatusBy = @byManual ,LastSync = NULL WHERE SubnetId = @SubnetId AND IpNodeId IN ( 10 ) ',N'@SubnetId int,@set int,@byManual smallint',@SubnetId=100,@set=4,@byManual=8192


The next query seems to update a count of available ip's in the subnet, so run this after reserving.  the @ids='100' is the subnet you updating.

exec sp_executesql N'exec dbo.IPAM_spTouchGroupCounts @ids',N'@ids varchar(3)',@ids='100'

And finally this inserts an event "log" that logs what has happened.  Everything in red you can change as you see fit.. the @ObjetID2 is important.  you need to lookup in the IPAM_Node table for this value.  The rest of the parameters leave as is.

exec sp_executesql N'INSERT INTO IPAM_EVENTS (EventTime,EventType,UserName,Message,ObjectID1,ObjectID2,ObjectType,Acknowledged)VALUES (GETDATE(),@EventType,@UserName,@Message,@ObjectID1,@ObjectID2,@ObjectType,0)',N'@EventType int,@UserName varchar(5),@Message ntext,@ObjectID1 int,@ObjectID2 uniqueidentifier,@ObjectType int',@EventType=942,@UserName='ADMIN',@Message=N'The IP ''192.168.0.9'' fields have been changed: Status to ''Reserved''',@ObjectID1=NULL,@ObjectID2='0900A8C0-0000-0000-0000-000000000000',@ObjectType=6


Thats it. it works for me, but no guarantees.

Level 9

Nice post! I am getting stuck on the options for status. From what I can see is the following

Status ID  Status Text

1               Used

2               Available

4               Reserved

Is there any other status?  Like should there be status ID 3?

Thanks,

Dusting

About the Author
I joined SolarWinds PM team in September 2011 and I am currently part of product management team located in Czech Republic . Before that, I spent five years developing networking products and mobile applications.