Using XML Lookup Files with Orchestrator

There can be many reasons for needing site specific data related to an IP address.  I had a recent requirement for Orchestrator to configure machines when the only input was the IP Address of the target machine.   This solution uses PowerShell to compare an IP Address to an XML based lookup table of site information.  By finding the right site, other locale details can be used through Orchestrator.  The XML below represents the site information needed by my particular process.

<data-set xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	<record>
		<Designation>Melbourne Workstations</Designation>
		<Zone>MWZ</Zone>
		<Description>Melbourne Workstation Zone</Description>
		<Network>192.168.2.0 / 24</Network>
		<SCCMSite>MEL</SCCMSite>
		<SCCMServer>sccmmel.domain.com</SCCMServer>
		<SCCMFSP>sccmfsp.domain.com</SCCMFSP>
	</record>
	<record>
		<Designation>Melbourne Servers</Designation>
		<Zone>MSZ</Zone>
		<Description>Melbourne Server Zone</Description>
		<Network>192.168.1.0 / 24</Network>
		<SCCMSite>MEL</SCCMSite>
		<SCCMServer>sccmmel.domain.com</SCCMServer>
		<SCCMFSP>sccmfsp.domain.com</SCCMFSP>
	</record>
	<record>
		<Designation>Melbourne Workstations</Designation>
		<Zone>ACIZ</Zone>
		<Description>Australia Core Infrastructure Zone</Description>
		<Network>192.168.3.0 / 24</Network>
		<SCCMSite>SYD</SCCMSite>
		<SCCMServer>sccmsyd.domain.com</SCCMServer>
		<SCCMFSP>sccmfsp.domain.com</SCCMFSP>
	</record>
</data-set>	

A real world example could have dozens of different sites.  The type of data cand be easily expanded but this example retrieves SCCM details for a particular location.  This XML lookup file is saved to "E:\NetworkData\Networks.xml" on my Orchestrator server.  Notice that this path is hardcoded within the powershell script that follows!

PowerShell Comparison of IP Addresses to Networks

TCP / IP works by assigning every device a unique address, identifying an address that can forward network traffic from a local site (a gateway).  The system allows a machine to identify what other machines are local or remote because of the subnet mask on that system.  The important thing to know is that networking (with TCP) is done in binary.

An IP Address of 192.168.1.35 can be represented with for binary elements instead.  The same address, when shown on four 8-bit registers would look like:

11000000  01000100 00000001 00011001

A subnet mask might commonly be represented as 255.255.255.0, which displayed as binary would as

11111111 11111111 11111111 00000000

The subnet mask is simply used to identify which part of a machine’s address identifies the network it’s on.  Another way of representing a subnet mask is represent the number of bits enabled so 255.255.255.0 is the same as 24 ones in a row and identical to showing the address as “192.168.1.35 / 24” 

As we know from the subnet mask that the first 24 bits of the address actually represent the network the machine is on, we can count that number of places to show that the actual network has a binary representation of:

11000000  01000100 00000001  

Workflow Process

To find details of the network my machine resides upon, I’ll do the same thing.  Powershell will:

  1. Take the IP address of a particular machine & convert each of the four decimal numbers into binary.  These are then joined to make binary string.
  2. Recursively loop through my XML file of known networks.  With each network, the actual binary address of the network is obtained and compared to the binary representation of the address I’m looking for.
  3. When the right network node is found, other details from that site may be returned.

Because Orchestrator doesn’t natively use PowerShell 3 (which I need for accessing the XML), the embedded script shells out to a new instance of PowerShell which does support the enhanced instruction set. 

Example Orchestrator Action

This PowerShell code is embedded within a "Run .Net Script" action.  The action received "InputIP" which is a vlid local IP address.  This particular action then uses a range of SCCM related variables as Published Data & use with other runbooks or activities.

# Example Script to gather network details for a Virtual Server
# Used for gathering zone details for any given machine
#
# Usage:  Input of a valid IP address to determine site details
# 
 
# A valid LAN IP
$inputIP = "\`d.T.~Ed/{8B583665-F1E8-4A18-A622-4F3EF8FA83BA}.{81F386A2-D492-4D20-942D-3B5EB7246339}\`d.T.~Ed/"
# eg. inputIP = "192.168.1.30"
 
#Create an input object to hold the IP Address to query
$VarObject = new-object pscustomobject -property @{ 
   IPADDRESS    = $inputIP
   ZONEDATAFILE = "E:\NetworkData\Networks.xml"
}
 
### Shell Out to Powershell 3
$psresult = $VarObject | Powershell {
 
#grab the piped object 
$inobject = $input | select -first 1
 
$inputIP         = $inobject.IPADDRESS
$ZoneDataFile    = $inobject.ZONEDATAFILE 
# Powershell 3 Code
 
#Path to te exported Network XML
[xml]$xml = Get-Content $ZoneDataFile
 
# Convert the input IP into binary
$inputBinary = ""
   $inputIP -split '\.' | ForEach-Object {
       $inputBinary = $inputBinary  + [System.Convert]::ToString($_,2).PadLeft(8,'0')
   }
 
#Recursively grab all addresses from known Networks from the xml
$nodes = Select-Xml "//record/Network" $xml
$nodes | ForEach-Object {
   $NetID = $_.Node.'#text'
 
   #split the string into IP and mask 
   $NetArray = $NetID.Split('/')
 
   #The network IP Address needs to be converted to binary
   $binRepresentation = ""
   $NetArray[0].Trim() -split '\.' | ForEach-Object {       
       $binRepresentation = $binRepresentation  + [System.Convert]::ToString($_,2).PadLeft(8,'0')
   }
 
   #subnet mask represents the length of the binary string
   $netmask = $binRepresentation.substring(0,$NetArray[1].Trim())
 
   # compare the complete binary string of the network to the binary string of the input address
   # to determine if the address is in that particular network
   # If so, write details about the zone to screen
 
   if ($inputBinary.StartsWith($netmask)){
 
     $RecordSet = $xml.'data-set'.record  | Where-Object  {$_.'Network' -eq $NetID}
     #$RecordSet
 
     $zone        = $RecordSet.'Zone'
     $Designation = $RecordSet.'Designation'
     $Description = $RecordSet.'Description'
     $SCCMSite    = $RecordSet.'SCCMSite'
     $SCCMServer  = $RecordSet.'SCCMServer'
     $SCCMFSP     = $RecordSet.'SCCMFSP'
     $Network    = $($NetID) 
   }   
}
 
# Create New object for returning data to Orchestrator
 
new-object pscustomobject -property @{ 
   ZONE = $zone
   SCCMSITE = $SCCMSite
   SCCMSERVER = $SCCMServer
   SCCMFSP = $SCCMFSP
   }
}
 
$zone       = $psresult.ZONE
$SCCMSite   = $psresult.SCCMSITE
$SCCMServer = $psresult.SCCMSERVER
$SCCMFSP    = $psresult.SCCMFSP