Share This Post

Part IV : Move Computers to OU based on IP Subnets

To automate the movement of computers in the Default Computers Container to the Location OUs based on their IP Subnets defined in the Active Directory Sites and Subnets

Source OU

ande.in/Computers

Destination OUs

ande.in/Asia/India/Computers

ande.in/Asia/Japan/Computers

ande.in/Europe/Italy/Computers

ande.in/North America/Canada/Computers

ande.in/South America/Argentina/Computers

Note :

1. We are going to use 3 Functions

Find-Site_Move and Get-Site_Move : Used to identify Subnet and Site Details. Credit goes to /\/\o\/\/

Split-Job : Used to speed up the script execution. So the script will execute 10 times faster. Credit goes to Arnoud Jansveld

2. Quest Active Directory cmdlets : Get Active directory data

3. Script execution time approx 100000 computers = 1 hour 30 mins

4. Modifications required in script:

$FileName : Choose path for the output file. [Default : C:\AD_Data_Move_16022010.log]

$Log : Choose path for the log file. [Default : C:\Log_16022010.log]

“SiteCode_India” Change the respective Site Code as in Active Directory Sites and Services

“ande.in/Asia/India/Computers” Change the respective OU

add-pssnapin Quest.ActiveRoles.ADManagement
 
$Log = Get-Date -uformat "C:\Log_%d%m%Y.log"
######################
$Date_Start = Get-Date
echo "$Date_Start : Finding Respective Site Code for All Computers....." | Out-File -Append -FilePath $Log
######################
##### Finding Respective Site Code for All Computers and Output to CSV
Function Find-Site_Move {
  param ([net.ipAddress]$ip)
  for ($bit = 30 ; $bit -ge 1; $bit--){
    [int]$octet = [math]::Truncate(($bit - 1 ) / 8)
    $net = [byte[]]@()
    for($o=0;$o -le 3;$o++) {
      $ba = $ip.GetAddressBytes()
      if ($o -lt $Octet) {
        $Net += $ba[$o]
      }ELSEIF ($o -eq $octet) {
        $factor = 8 + $Octet * 8 - $bit
        $Divider = [math]::pow(2,$factor)
        $value = $divider * [math]::Truncate($ba[$o] /  $divider)
        $Net += $value
      }ELSE {
        $Net += 0
      }
      }
    $NetWork = [string]::join('.',$net) + "/$bit"
 
    if ($verbose.IsPresent) {write-host -fore 'green' "Trying : $network"}
    $de = New-Object directoryservices.directoryentry('LDAP://rootDSE')
    $Root = New-Object directoryservices.directoryentry("LDAP://$($de.configurationNamingContext)")
    $ds = New-Object directoryservices.directorySearcher($root)
    $ds.filter = "(CN=$NetWork)"
    $r = $ds.findone()
    if ($r) {
    $Site_C = $r.GetDirectoryEntry().siteObject
    $2Name = "$Site_C"
    $Site_Code = $2Name.split('=,')[1]
    $Output= "$Computer" + ',' + "$Site_Code"
    break}}
    if ($Output){$Output}
    else {"$Computer" + ',' + "NoSite"}
}
Function Get-Site_Move {
  param ($Computer)
  $ip = 0
    &{TRAP{$ip = $null;continue}
      $ip =[System.Net.Dns]::GetHostAddresses($Computer)
      if ($ip) {
        $ips = "$ip"
        Find-Site_Move $ips
      }ELSE {
        "$Computer" + ',' + "NoSite"
      }
      }
      }
#######################
function Split-Job {
    param (
        $Scriptblock = $(throw 'You must specify a command or script block!'),
        [int]$MaxPipelines=10,
        [switch]$UseProfile,
        [string[]]$Variable,
        [string[]]$Function = @(),
        [string[]]$Alias = @(),
        [string[]]$SnapIn
    )
 
    function Init ($InputQueue){
        # Create the shared thread-safe queue and fill it with the input objects
        $Queue = [Collections.Queue]::Synchronized([Collections.Queue]@($InputQueue))
        $QueueLength = $Queue.Count
        # Do not create more runspaces than input objects
        if ($MaxPipelines -gt $QueueLength) {$MaxPipelines = $QueueLength}
        # Create the script to be run by each runspace
        $Script  = "Set-Location '$PWD'; "
        $Script += {
            $SplitJobQueue = $($Input)
            & {
                trap {continue}
                while ($SplitJobQueue.Count) {$SplitJobQueue.Dequeue()}
            } |
        }.ToString() + $Scriptblock
 
        # Create an array to keep track of the set of pipelines
        $Pipelines = New-Object System.Collections.ArrayList
 
        # Collect the functions and aliases to import
        $ImportItems = ($Function -replace '^','Function:') +
            ($Alias -replace '^','Alias:') |
            Get-Item | select PSPath, Definition
        $stopwatch = New-Object System.Diagnostics.Stopwatch
        $stopwatch.Start()
      }
 
    function Add-Pipeline {
        # This creates a new runspace and starts an asynchronous pipeline with our script.
        # It will automatically start processing objects from the shared queue.
        $Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($Host)
        $Runspace.Open()
        $Runspace.SessionStateProxy.SetVariable('SplitJobRunSpace', $True)
 
        function CreatePipeline {
            param ($Data, $Scriptblock)
            $Pipeline = $Runspace.CreatePipeline($Scriptblock)
            if ($Data) {
                $Null = $Pipeline.Input.Write($Data, $True)
                $Pipeline.Input.Close()
      }
            $Null = $Pipeline.Invoke()
            $Pipeline.Dispose()
      }
 
        # Optionally import profile, variables, functions and aliases from the main runspace
        if ($UseProfile) {
            CreatePipeline -Script "`$PROFILE = '$PROFILE'; . `$PROFILE"
      }
        if ($Variable) {
            foreach ($var in (Get-Variable $Variable -Scope 2)) {
                trap {continue}
                $Runspace.SessionStateProxy.SetVariable($var.Name, $var.Value)
      }
      }
        if ($ImportItems) {
            CreatePipeline $ImportItems {
                foreach ($item in $Input) {New-Item -Path $item.PSPath -Value $item.Definition}
      }
      }
        if ($SnapIn) {
            CreatePipeline (Get-PSSnapin $Snapin -Registered) {$Input | Add-PSSnapin}
      }
        $Pipeline = $Runspace.CreatePipeline($Script)
        $Null = $Pipeline.Input.Write($Queue)
                $Pipeline.Input.Close()
        $Pipeline.InvokeAsync()
        $Null = $Pipelines.Add($Pipeline)
      }
 
    function Remove-Pipeline ($Pipeline) {
        # Remove a pipeline and runspace when it is done
        $Pipeline.RunSpace.Close()
            $Pipeline.Dispose()
        $Pipelines.Remove($Pipeline)
      }
 
    # Main
    # Initialize the queue from the pipeline
    . Init $Input
    # Start the pipelines
    while ($Pipelines.Count -lt $MaxPipelines -and $Queue.Count) {Add-Pipeline}
 
    # Loop through the runspaces and pass their output to the main pipeline
    while ($Pipelines.Count) {
        # Only update the progress bar once a second
        if (($stopwatch.ElapsedMilliseconds - $LastUpdate) -gt 1000) {
            $Completed = $QueueLength - $Queue.Count - $Pipelines.count
            $LastUpdate = $stopwatch.ElapsedMilliseconds
            $SecondsRemaining = $(if ($Completed) {
                (($Queue.Count + $Pipelines.Count)*$LastUpdate/1000/$Completed)
            } else {-1})
            Write-Progress 'Split-Job' ("Queues: $($Pipelines.Count)  Total: $($QueueLength)  " +
            "Completed: $Completed  Pending: $($Queue.Count)")  `
            -PercentComplete ([Math]::Max((100-[Int]($Queue.Count+$Pipelines.Count)/$QueueLength*100),0)) `
            -CurrentOperation "Next item: $(trap {continue}; if ($Queue.Count) {$Queue.Peek()})" `
            -SecondsRemaining $SecondsRemaining
      }
        foreach ($Pipeline in @($Pipelines)) {
            if ( -not $Pipeline.Output.EndOfPipeline -or -not $Pipeline.Error.EndOfPipeline ) {
                $Pipeline.Output.NonBlockingRead()
                $Pipeline.Error.NonBlockingRead() | Out-Default
            } else {
                # Pipeline has stopped; if there was an error show info and restart it
                if ($Pipeline.PipelineStateInfo.State -eq 'Failed') {
                    $Pipeline.PipelineStateInfo.Reason.ErrorRecord |
                        Add-Member NoteProperty writeErrorStream $True -PassThru |
                            Out-Default
                    # Restart the runspace
                    if ($Queue.Count -lt $QueueLength) {Add-Pipeline}
      }
                Remove-Pipeline $Pipeline
      }
      }
        Start-Sleep -Milliseconds 100
      }
}
######################
$FileName = Get-Date -uformat 'C:\AD_Data_Move_%d%m%Y.log'
get-QADComputer -searchroot "ande.in/Computers" -SizeLimit 0 | select Name `
| Split-Job -Function "Get-Site_Move","Find-Site_Move" {%{ (Get-Site_Move $_.Name) }} `
| Out-File -Append -FilePath $FileName
 
 
######################
$Date_Move = Get-Date
echo "$Date_Move : Moving Computers to Respective Site OU....." | Out-File -Append -FilePath $Log
######################
##### Moving StaleOU Computers to Location Specific Stale OU
 
$OU_Names = @{"SiteCode_India" = "ande.in/Asia/India/Computers";
"SiteCode_Japan" = "ande.in/Asia/Japan/Computers";
"SiteCode_Italy" = "ande.in/Europe/Italy/Computers";
"SiteCode_Canada" = "ande.in/North America/Canada/Computers";
"SiteCode_Argentina" = "ande.in/South America/Argentina/Computers";}
 
$Sites ='SiteCode_India','SiteCode_Japan','SiteCode_Italy','SiteCode_Canada','SiteCode_Argentina'
foreach ($Site in $Sites)
{
$SearchOU ="ande.in/Computers"
$DestOU = $OU_Names.$Site
$StaleOU = "StaleOU_$Site"
$StaleOU = get-content $FileName `
|select-object @{e={$_.split('",')[0]};n='Name'},@{e={$_.split(',"')[1]};n='Site'} `
|Where-Object {$_.Site -eq $Site}
 
foreach ($S in $StaleOU)
{
Get-QADComputer -SearchRoot $SearchOU -Name $S.Name | Move-QADObject -ErrorAction SilentlyContinue -NewParentContainer $DestOU
}
}
######################
$Date_Finish = Get-Date
echo "$Date_Finish : Movement Completed Successfully....." | Out-File -Append -FilePath $Log
######################

Share This Post

Leave a Reply