Part IV : Move Computers to OU based on IP Subnets [ http://ande.in ]

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
######################
Published Wednesday, March 31, 2010 5:52 AM by andersonk

Comments

No Comments