Skip to content

Instantly share code, notes, and snippets.

@darrenjrobinson
Last active February 2, 2016 06:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darrenjrobinson/eb93a53affb4601c0116 to your computer and use it in GitHub Desktop.
Save darrenjrobinson/eb93a53affb4601c0116 to your computer and use it in GitHub Desktop.
MIMUsrPlacementPSMA-Import
param (
$Username,
$Password,
$OperationType,
[bool] $usepagedimport,
$pagesize
)
#Needs reference to .NET assembly used in the script.
Add-Type -AssemblyName System.DirectoryServices.Protocols
$CookieFile = "C:\PROGRA~1\MICROS~4\2010\SYNCHR~1\EXTENS~2\Placement\Placement\dn.bin"
$DebugFilePath = "C:\PROGRA~1\MICROS~4\2010\SYNCHR~1\EXTENS~2\Placement\Placement\DebugOUMA.txt"
# The OU where we land the new users. The ones that then need to be moved
$landingOU = "OU=MIM,OU=customer,DC=domain,DC=com,DC=au"
# The OU where expired users are placed before deletion
$expiredOU = "OU=Expired Accounts,OU=customer,DC=domain,DC=com,DC=au"
#Getting Cookie from file
If (Test-Path $CookieFile –PathType leaf) {
[byte[]] $Cookie = Get-Content -Encoding byte –Path $CookieFile
} else {
$Cookie = $null
}
if(!(Test-Path $DebugFilePath))
{
$DebugFile = New-Item -Path $DebugFilePath -ItemType File
}
else
{
$DebugFile = Get-Item -Path $DebugFilePath
}
"Starting Import : " + (Get-Date) | Out-File $DebugFile -Append
#region User
$Properties = @("objectguid","title", "st", "distinguishedName", "sAMAccountName","isDeleted")
#Running as FIM MA Account
$Credentials = New-Object System.Net.NetworkCredential($username,$password)
$RootDSE = [ADSI]"LDAP://RootDSE"
$LDAPDirectory = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($RootDSE.dnsHostName)
$LDAPConnection = New-Object System.DirectoryServices.Protocols.LDAPConnection($LDAPDirectory, $Credentials)
$Request = New-Object System.DirectoryServices.Protocols.SearchRequest($RootDSE.defaultNamingContext, "(&(objectClass=user)(sAMAccountName=u*))", "Subtree", $Properties)
#Defining the object type returned from searches for performance reasons.
[System.DirectoryServices.Protocols.SearchResultEntry]$entry = $null
if ($OperationType -eq "Full")
{
$Cookie = $null
}
else
{
# delta run and we should use the cookie we already found
}
$DirSyncRC = New-Object System.DirectoryServices.Protocols.DirSyncRequestControl($Cookie, [System.DirectoryServices.Protocols.DirectorySynchronizationOptions]::IncrementalValues, [System.Int32]::MaxValue)
$Request.Controls.Add($DirSyncRC) | Out-Null
$MoreData = $true
$Guids = @()
while ($MoreData) {
$Response = $LDAPConnection.SendRequest($Request)
ForEach($entry in $Response.Entries){
#Check if this GUID already been handled to avoid adding duplicate objects
If($Guids -contains ([GUID] $entry.Attributes["objectguid"][0]).ToString()){continue}
# we always add objectGuid and objectClass to all objects
$obj = @{}
$obj.Add("objectGuid", ([GUID] $entry.Attributes["objectguid"][0]).ToString())
$obj.Add("objectClass", "user")
if ( $entry.distinguishedName.Contains("CN=Deleted Objects"))
{
# this is a deleted object, so we return a changeType of 'delete'; default changeType is 'Add'
$obj.Add("changeType", "Delete")
}
else
{
# we need to get the directory entry to get the additional attributes since
$DirEntry = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$($entry.distinguishedName)"
$obj.Add("accountName",$DirEntry.Properties["sAMAccountName"][0])
# setting the values for attributes defined in the schema
if ($DirEntry.Properties["title"][0])
{
$obj.Add("title",$DirEntry.Properties["title"][0])
$searchTitle = $DirEntry.Properties["title"][0]
}
if ($DirEntry.Properties["st"][0])
{
$obj.Add("state",$DirEntry.Properties["st"][0])
$searchState = $DirEntry.Properties["st"][0]
}
if ($DirEntry.Properties["distinguishedName"][0])
{
$obj.Add("distinguishedName",$DirEntry.Properties["distinguishedName"][0])
}
if ($DirEntry.Properties["cn"][0])
{
$obj.Add("cn",$DirEntry.Properties["cn"][0])
}
if ($DirEntry.Properties["userPrincipalName"][0])
{
$obj.Add("upn",$DirEntry.Properties["userPrincipalName"][0])
}
# *********************************
# Go out and search for existing users matching the current users Title and State.
$SearchProperties = @("objectguid","title", "st", "distinguishedName", "sAMAccountName","isDeleted")
#Running as FIM MA Account
$SearchCredentials = New-Object System.Net.NetworkCredential($username,$password)
$SearchRootDSE = [ADSI]"LDAP://RootDSE"
$SearchLDAPDirectory = New-Object System.DirectoryServices.Protocols.LdapDirectoryIdentifier($SearchRootDSE.dnsHostName)
$SearchLDAPConnection = New-Object System.DirectoryServices.Protocols.LDAPConnection($SearchLDAPDirectory, $SearchCredentials)
$SearchRequest = New-Object System.DirectoryServices.Protocols.SearchRequest($SearchRootDSE.defaultNamingContext, "(&(objectClass=user)(sAMAccountName=u*)(title=$searchTitle)(st=$searchState))", "Subtree", $SearchProperties)
#Defining the object type returned from searches for performance reasons.
[System.DirectoryServices.Protocols.SearchResultEntry]$SearchEntry = $null
$ArrayListOU = @()
# Only search for users that are in the landing OU
if ($DirEntry.Properties["distinguishedName"][0].Contains($landingOU))
{
# Only Search if we have attribute vlaues to search on
if ($searchTitle -and $searchState)
{
$SearchResponse = $SearchLDAPConnection.SendRequest($SearchRequest)
ForEach($searchEntry in $SearchResponse.Entries)
{
$matchedUserOU = $searchEntry.DistinguishedName | ForEach-Object {
# drop the CN from the object to get just the OU
$_ -replace '^.+?(?<!\\),',''
# Add the OU to the array
$ArrayListOU += ,$matchedUserOU
}
}
# How many results do we have ? Must have as at least one match, as one will be out actual user
# if we have more than one match there we have a candidate
if ($ArrayListOU.Count -gt 1)
{
# interate through the Array to exlude the LandingOU and the Expired Accounts OU
# Order the list by the most occurances and select the top entry
$targetOU = $ArrayListOU | Group-Object | Sort-Object -Descending {$_.count} | Where-Object {$_.Name -ne $landingOU} | Where-Object {$_.Name -ne $expiredOU} | Select-Object $_.Name -First 1
# after filtering out and sorting do we have somewhere to place the user
if ($targetOU.name -match 'DC=domain,DC=com,DC=au')
{
# We have somewhere to move out user to
$obj.Add("targetOU",$targetOU.name)
}
else
{
# nowhere to put the user with similar colleages. Geography catch-all
switch($searchState)
{
"NSW" {$targetOU = "OU=NSW,OU=customer,DC=domain,DC=com,DC=au"}
"QLD" {$targetOU = "OU=QLD,OU=customer,DC=domain,DC=com,DC=au"}
"VIC" {$targetOU = "OU=VIC,OU=customer,DC=domain,DC=com,DC=au"}
"TAS" {$targetOU = "OU=TAS,OU=customer,DC=domain,DC=com,DC=au"}
"ACT" {$targetOU = "OU=ACT,OU=customer,DC=domain,DC=com,DC=au"}
"NT" {$targetOU = "OU=NT,OU=customer,DC=domain,DC=com,DC=au"}
"WA" {$targetOU = "OU=WA,OU=customer,DC=domain,DC=com,DC=au"}
"SA" {$targetOU = "OU=SA,OU=customer,DC=domain,DC=com,DC=au"}
"UK" {$targetOU = "OU=UK,OU=customer,DC=domain,DC=com,DC=au"}
default {$targetOU = "OU=Other,OU=customer,DC=domain,DC=com,DC=au"}
}
$obj.Add("targetOU",$targetOU)
}
}
else
{
#Damn the user my have a unique position within their state.
# Drop them in a State based OU. They can be manually moved if required
switch($searchState)
{
"NSW" {$targetOU = "OU=NSW,OU=customer,DC=domain,DC=com,DC=au"}
"QLD" {$targetOU = "OU=QLD,OU=customer,DC=domain,DC=com,DC=au"}
"VIC" {$targetOU = "OU=VIC,OU=customer,DC=domain,DC=com,DC=au"}
"TAS" {$targetOU = "OU=TAS,OU=customer,DC=domain,DC=com,DC=au"}
"ACT" {$targetOU = "OU=ACT,OU=customer,DC=domain,DC=com,DC=au"}
"NT" {$targetOU = "OU=NT,OU=customer,DC=domain,DC=com,DC=au"}
"WA" {$targetOU = "OU=WA,OU=customer,DC=domain,DC=com,DC=au"}
"SA" {$targetOU = "OU=SA,OU=customer,DC=domain,DC=com,DC=au"}
"UK" {$targetOU = "OU=UK,OU=customer,DC=domain,DC=com,DC=au"}
default {$targetOU = "OU=Other,OU=customer,DC=domain,DC=com,DC=au"}
}
}
$obj.Add("targetOU",$targetOU)
}
}
}
else
# user has been moved or doesn't need to be moved
# put the current OU in the targetOU attribue so that the Placement OU Sync Rule Outbound doesn't complain about a null value.
{
$targetOU = $DirEntry.Properties["distinguishedName"][0] | ForEach-Object {
$_ -replace '^.+?(?<!\\),',''
}
$obj.add("targetOU",$targetOU)
}
}
#Add Guid to list of processed guids to avoid duplication
$Guids += ,([GUID] $entry.Attributes["objectguid"][0]).ToString()
#Return the object to the MA
$obj
}
ForEach ($Control in $Response.Controls) {
If ($Control.GetType().Name -eq "DirSyncResponseControl") {
$Cookie = $Control.Cookie
$MoreData = $Control.MoreData
}
}
$DirSyncRC.Cookie = $Cookie
}
#Saving cookie file
Set-Content -Value $Cookie -Encoding byte –Path $CookieFile
$global:RunStepCustomData = [System.Convert]::ToBase64String($Cookie)
#endregion
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment