Use PowerShell to Move Computers Based on IP Addresses: Part 1

Doctor Scripto

Summary: In this guest blog post written by Eric Wright, you will learn how to use the Windows PowerShell snap-in, Quest ActiveRoles, to move computers that are organized in Active Directory, based on their IP addresses.

Microsoft Scripting Guy, Ed Wilson, is here. Guest Blogger Weekend continues. Today we have a guest blog written by Eric Wright. Here is what Eric has to say about himself.

Photo of Eric Wright

I am a systems architect and blogger, and I work with Microsoft tools, Windows PowerShell, virtualization, and various web technologies. I’m a big fan of automation and scripting to simplify and enhance systems administration.
Contact information:
Website: DiscoPosse—Using the chicken to measure IT
Twitter: http://www.twitter.com/discoposse
LinkedIn: http://ca.linkedin.com/pub/eric-wright/3/7b4/bb6

Note: This script uses the Windows PowerShell snap-in, Quest ActiveRoles. This process will work with any version of Windows Server, but you must use Quest ActiveRoles if you are running a version earlier than Windows Server 2008 R2 on your domain controllers. Tomorrow in Part 2 of this blog, I document the exact process, but we use Active Directory domain controllers with Windows Server 2008 R2 or the Active Directory Management Gateway Service.

In my organization, I have chosen to organize my Active Directory (AD) organizational unit (OU) structure based on physical locations. A common challenge is that our technical support team does not always move computer accounts into the proper structure in Active Directory. Another issue is that computers may not be deleted from the domain when they are decommissioned. This confuses other processes that use Active Directory as their authoritative source for computer object information.

To tackle this issue, I created a Windows PowerShell script that runs as a batch process and will move the computer objects into OUs based on their IP addresses.

In my example, I am looking for only Windows 7 computers, but this can be flavored to match any selection criteria you need. The structure of the script is to do the following:

  1. Check the operating system for Windows 7 (any version).
  2. Check to see if the computer has been off the domain.
  3. If the computer has been off the network for 60 days, move it to a “Disabled” OU.
  4. If the computer has been off the network for 90 days, delete it.
  5. Check for the last DNS registration of the computer, and move it to an OU based on its IP information.

For our script to work, we need to have the Windows PowerShell snap-in, Quest ActiveRoles, installed on the computer that will be running the script for us. 

If you are running Active Directory Domain Controllers with Windows Server 2008 R2, you can use the native ActiveDirectory Windows PowerShell module. I will post the script for using the Windows Server 2008 R2 module tomorrow in part 2 of this series.

We will also need to define the IP subnets and the OU structure so that we can match the computer object’s IP information and move it to its correct location in AD.

First we load the Quest ActiveRoles snap-in as shown here:

Add-PsSnapIn Quest.ActiveRoles.ADManagement -ErrorAction SilentlyContinue

Next, we want to define two parameters for the age of the computers. I call these $old and $veryold, and for my example, I have set them as 60 days and 90 days respectively. You can adjust these easily to suit your needs.

$old = (Get-Date).AddDays(-60) # Modify the -60 to match your threshold

$veryold = (Get-Date).AddDays(-90) # Modify the -90 to match your threshold 

Now the fun part! Because will capture the IP information as a string and not as an integer, this makes it a bit more challenging to figure out what subnet we are in. This example has three subnets: 192.168.1.0/24, 192.168.2.0/24, and 192.168.3.0/24. I have chosen class C subnets for this script to match my structure, but you may have to get more creative if you have a more complex network configuration.

We will define our IP range variables as regular expressions (or Regex as they are commonly known), so that we can match the characters appropriately. Sorry kids, but it is goodbye GUI and hello Regex for this stuff.

$Site1IPRange = “\b(?:(?:192)\.)” + “\b(?:(?:168)\.)” + “\b(?:(?:1)\.)” + “\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))” # 192.168.1.0/24

$Site2IPRange = “\b(?:(?:192)\.)” + “\b(?:(?:168)\.)” + “\b(?:(?:2)\.)” + “\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))” # 192.168.2.0/24

$Site3IPRange = “\b(?:(?:192)\.)” + “\b(?:(?:168)\.)” + “\b(?:(?:3)\.)” + “\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))” # 192.168.3.0/24

I know you are probably thinking it is time to just retrain the support staff to do this, right? Do not be frightened away just yet. Regex is easier than you may think once you use it more and can break it down into sensible chunks. It is as simple as reading a map (OK, that is not always simple).

Here Be Regex Dragons!

 Image of map

The key information we see here is pretty readable. Because we know the first three octets are static, we define them easily, as follows:

“\b(?:(?:192)\.)” + “\b(?:(?:168)\.)” + “\b(?:(?:1)\.)”

This shows us matching as 192.168.1., which takes care of the first three octets. Because it is a class C IP range, we want to capture from 0-255 in the fourth octet, which is done like this:

“\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))”

We read the string and look for matching of three distinct ranges, which are 0-199, 200-249, or 250-255. The OR is the important part of the phrasing, and it is represented by the | symbol, which is known as the “pipe” symbol to most.

Here is the breakdown of those three ranges:

250-255: 25[0-5]

200-249: 2[0-4][0-9]

0-199: [01]?[0-9][0-9]?

 OK, we are through the tough part. Now we define the OU structure for the Disabled and the three locations:

$DisabledDN = “OU=Disabled,DC=yourdomain,DC=com”

$Site1DN = “OU=Site1,DC=yourdomain,DC=com”

$Site2DN = “OU=Site2,DC=yourdomain,DC=com”

$Site3DN = “OU=Site3,DC=yourdomain,DC=com”

This is where we begin the object query. Because we want to find out how “old” the computer account is, we will bring in the pwdLastSet property from Active Directory, in addition to the default values. (Note that the Quest ActiveRoles parameter uses pwdLastSet, whereas the native Microsoft parameter in tomorrow’s blog uses PasswordLastSet.) This will tell us the last time the hidden password that is negotiated between the computer account and Active Directory has been reset. The default maximum duration is 30 days, so as long as a computer is connecting to the domain regularly, it should always be less than 30 days old.

If you want to modify the operating systems that get captured, you simply change the selection parameters of the Get-QADComputer query as shown here:

Get-QADComputer -ComputerRole member -IncludedProperties pwdLastSet -SizeLimit 0 -OSName ‘Windows 7*’ | ForEach-Object { THE REST OF OUR SCRIPT GOES IN HERE }

Our script will query AD for each Computer object, and we will run the next bunch of processes against each object in the ForEach-Object loop. All of the following content is stored inside the curly brackets.

Let’s ignore any failure messages from the IP lookup with this:

trap [System.Net.Sockets.SocketException] { continue; }

We need to use the Computer name, DN, and pwdLastSet, so let’s set those as variables from the query result. We also want to capture the current container, so we use a simple Replace command to derive the current OU location:

$ComputerName = $_.Name

$ComputerDN = $_.DN

$ComputerPasswordLastSet = $_.pwdLastSet

$ComputerContainer = $ComputerDN.Replace( “CN=$ComputerName,” , “”)

 Now we can work with the Computer account age and delete or move them as necessary:

# If the computer is more than 90 days off the network, remove the computer object

if ($ComputerPasswordLastSet -le $veryold) {

            Remove-QADObject -Identity $ComputerDN

}

# Check to see if it is an “old” computer account and move it to the Disabled\Computers OU

if ($ComputerPasswordLastSet -le $old) {

$DestinationDN = $DisabledDN

Move-QADObject -Identity $ComputerDN -NewParentContainer $DestinationDN

}

Next, we query DNS for the IP address of the computer. We will set the $IP value as $NULL first, so that if the query fails, it will be dealt with correctly later in the process. If we don’t set the NULL value, it retains the IP from the last lookup, and it will move the computer incorrectly.

$IP = $NULL

$IP = [System.Net.Dns]::GetHostAddresses(“$ComputerName”)

Now it is time to check for the IP range to set the destination DN accordingly. If you have a majority of systems in some network ranges, you may want to move those up to the top of the If statement so that they are processed early, which will save some time.

if ($IP -match $Site1IPRange) {

            $DestinationDN = $Site1DN

}

ElseIf ($IP -match $Site2IPRange) {

            $DestinationDN = $Site2DN

}

ElseIf ($IP -match $Site3IPRange) {

            $DestinationDN = $Site3DN

}

Else {

            # If the subnet does not match we should not move the computer so we do nothing

            $DestinationDN = $ComputerContainer

}

Let’s do a health check on our IP selection:

 Image of command output

And here is the last step to actually move the object to the new destination OU. This is where our NULL IP comes into play because we have assumed that if the IP is NULL, it is “off network” and the aged account process has already dealt with it:

if ($IP -ne $NULL) {

            Move-QADObject -Identity $ComputerDN -NewParentContainer $DestinationDN

}

And we made it! Another exciting tip with this script is that you can run all of the QADObject cmdlets with the WhatIf parameter, which will output the result to the screen rather than perform the move or delete, so you can test drive the script before you implement it.

You can download the script in its full form from the TechNet Resources Gallery.

~Eric

Thank you, Eric. This is a really cool solution to a rather common problem. I am looking forward to part 2 tomorrow from Eric.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

0 comments

Discussion is closed.

Feedback usabilla icon