How to Use PowerShell to bulk load AD LDS groups and their membership into FIM using LDAP queries
FIM ScriptBox Item
Summary
This script was developed to bulk load groups and their membership from AD LDS and import them into FIM.
Script Code
# Title: Group Bulk Load from LDAP Query
# Author: Justin Watts
# Email: JustinW1231@gmail.com
#
# Version: 1.5
#
# Description:
# This script is designed to bulk load groups in AD LDS through LDAP queries into FIM Portal which will be managed as a security groups.
#
#
# Options
#
#Default Owners in group to be converted to FIM Resource ID's
$DefaultOwners = @("UID1","UID")
#FIM Connection Properties
$URI = "http://FIMHost.domain.com:5725/resourcemanagementservice"
#LDAP Connection Properties
$LDAPServer = "LDAPServer.domain.com"
$LDAPSPort = "389"
$queryLimit = "0" #Number of entries that may be returned
$pageSize = "500" #Number of results pulled at one time. There is a 60 second timeout per result set.
#Commonly used Base DN's for queries
$B2EOU = "OU=PortalGroups,OU=Applications,DC=domain,DC=com"
$PeopleOU = "OU=People,DC=domain,DC=com"
#Logging
$ApplicationFilePath = Resolve-Path .
$GroupLog = "$ApplicationFilePath\Groupsprocessed.csv"
$UserLog = "$ApplicationFilePath\Usersprocessed.csv"
$Logfile = "$ApplicationFilePath\GroupMigration.log"
#
# Functions
#
Function UnWrap-ADQueryResults
{
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[Object[]]$LDAPSearchResults)
#Initializing
$LDAPResults = @()
foreach ($Result in $LDAPSearchResults)
{
$tempObj = @{}
$LDAPReturn = $Result.properties
foreach ($LDAPAttribute in $LDAPReturn.propertyNames)
{
#Processing for multivalued attributes
If(@($LDAPReturn.item($LDAPAttribute)).count -gt 1)
{
$LDAPValue = $LDAPReturn.item($LDAPAttribute)
$Values = @()
ForEach($Value in $LDAPValue)
{
$Values += $Value
}
$tempObj.add($LDAPAttribute,$Values)
}
#Processing for single valued attributes
Else
{
$tempObj.add($LDAPAttribute,($LDAPReturn.item($LDAPAttribute) | Out-String).trim())
}
}
#Creating variable for multivalue attributes collected in $tempObj
$Result = [pscustomobject]$tempObj
#Adding single valued attributes to result set.
$LDAPResults += $Result
}
Return $LDAPResults
}
Function LDAPSearch
{
PARAM($Attributes,
$filter,
$queryLimit,
$pageSize,
$restrictOU,
$baseDN)
END
{
$LDAPSearch = New-Object DirectoryServices.DirectorySearcher
$LDAPSearch.SizeLimit = $queryLimit
$LDAPSearch.PageSize = $pageSize
$LDAPSearch.Filter = $filter
Foreach($Attribute in $Attributes){$LDAPSearch.PropertiesToLoad.Add("$Attribute")}
If($restrictOU){$LDAPSearch.SearchRoot = "LDAP://$LDAPServer" + ":$LDAPPort/" + $restrictOU + ",$baseDN"}
Else{$LDAPSearch.SearchRoot = "LDAP://$LDAPServer" + ":$LDAPPort/" + $baseDN}
$LDAPSearchResults = UnWrap-ADQueryResults -LDAPSearchResults $LDAPSearch.Findall()
$LDAPSearchResults
}
}
function GetAttribute
{
PARAM($exportObject,
[string] $name)
END
{
$attribute = $exportObject.ResourceManagementObject.ResourceManagementAttributes |
Where-Object {$_.AttributeName -eq $name}
if ($attribute -ne $null -and $attribute.Value) {
$attribute.Value
}
}
}
function CreateObject
{
PARAM($objectType)
END
{
$newObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
$newObject.ObjectType = $objectType
$newObject.SourceObjectIdentifier = [System.Guid]::NewGuid().ToString()
$newObject
}
}
function SetAttribute
{
PARAM($object,
$attributeName,
$attributeValue)
END
{
write-host $attributeName $attributeValue
$importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange
$importChange.Operation = 1
$importChange.AttributeName = $attributeName
$importChange.AttributeValue = $attributeValue
$importChange.FullyResolved = 1
$importChange.Locale = "Invariant"
if ($object.Changes -eq $null) {$object.Changes = (,$importChange)}
else {$object.Changes += $importChange}
}
}
function ModifyObject
{
PARAM($objectType,
$target)
END
{
$changeObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
$changeObject.ObjectType = $objectType
$changeObject.TargetObjectIdentifier = $target
$changeObject.SourceObjectIdentifier = $target
$changeObject.State = 1
$changeObject
}
}
function AddMultiValue
{
PARAM($object,
$AttributeName,
$AttributeValue)
END
{
$importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange
$importChange.Operation = 0
$importChange.AttributeName = $AttributeName
$importChange.AttributeValue = $AttributeValue
$importChange.FullyResolved = 0
$importChange.Locale = "Invariant"
if ($object.Changes -eq $null) {$object.Changes = (,$importChange)}
else {$object.Changes += $importChange}
}
}
function UserLog
{
PARAM($msg)
END
{
Add-Content -Path $UserLog -Encoding ASCII -value $msg
write-host $msg
}
}
function GroupLog
{
PARAM($msg)
END
{
Add-Content -Path $GroupLog -Encoding ASCII -value $msg
write-host $msg
}
}
function WriteLog
{
PARAM($msg)
END
{
$date = (Get-Date).ToString()
$recordMsg = $date + ": " + $msg
Add-Content -Path $LogFile -Encoding ASCII -value $recordMsg
write-host $msg
}
}
#
# Main
#
WriteLog "Starting Script."
#Initialize
Clear-Host
$Error.Clear()
#Adding FIMAutomation Snapin
if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
#Initialize FIM Credentials used throughout the script.
$FIMInitialize = export-fimconfig -uri $URI `
–onlyBaseResources `
-Credential $userCreds `
-customconfig "/Person[DisplayName = 'FIMPortalAdmin']"
#Initialize Owner members to Group reference
$ownerM2Gtable = @{}
#Search for all -AdminGroup groups in attached LDAP directory.
WriteLog "LDAP Search for (*-AdminGroup)"
$groupAdminGroup = LDAPSearch -filter "(*-AdminGroup)" `
-Attributes @("cn", "distinguishedName")`
-baseDN $B2EOU
If($Error)
{
WriteLog "Error(s) identified in initialization:"
foreach ($msg in $Error){WriteLog "$msg`r`n--------"}
}
#To iterate through -AdminGroup names and establish a table of memberships for owner groups
Foreach ($adminGroup in $groupAdminGroup.distinguishedName)
{
#Reset Errors for -AdminGroup processing
$Error.Clear()
#Initialize owner membership array
$ownerMembership = @()
#Identify Group Names by removing the OU structure and CN= prefix
$ownerGroupParse = $adminGroup -Split(",OU=")
$ownerGroupName = $ownerGroupParse[0] -Split("CN=") -Split("-AdminGroup")
#Searching for users in People OU, containing memberOf the Admin Group DN.
$adminGroup
$groupAdminMembership = LDAPSearch -Filter "(&(objectClass=Person)(memberOf=$adminGroup))"`
-Attributes @("cn","distinguishedName")`
-baseDN $PeopleOU
If($groupAdminMembership.cn)
{
#If users exists in -AdminGroup group, create a reference hashtable ($ownerM2Gtable) associating members to group name.
Foreach($ownerMember in $groupAdminMembership.cn)
{
$ownerMembership += $ownerMember
}
$ownerM2Gtable.add($ownerGroupName[1],$ownerMembership)
}
Else
{
#Use default owners for -AdminGroup group
WriteLog "No owners found for $adminGroup! Using default Owners."
$ownerM2Gtable.add($ownerGroupName[1],$DefaultOwners)
}
If($Error)
{
WriteLog "Error identified in owner table processing for $adminGroup."
foreach($msg in $Error){WriteLog "$msg`r`n--------"}
}
}
#-AdminGroup membership gathered. Display hash table.
$ownerM2Gtable
$memberOU2FGtable = @{}
$FIMURNConvert = @{}
#Initialize FIMURNConvert table. All members and owners will be managed here to avoid querying the web server
Foreach($ownerGroup in $ownerM2Gtable.Keys)
{
$Error.Clear()
#Search for membership groups, containing object class group AND $ownerM2Gtable.Key AND not containing *-AdminGroup"
WriteLog "Conducting LDAP Search on B2EUserGroup $ownerGroup*"
$B2EUserGroups = LDAPSearch -Filter "(&(objectClass=Group)(cn=$ownerGroup*)(!(cn=*-AdminGroup)))"`
-Attributes @("name","distinguishedName")`
-baseDN $B2EOU
If($Error)
{
WriteLog "Error(s) identified in member group LDAP Query for: $ownerGroup."
foreach($msg in $Error){WriteLog "$msg`r`n--------"}
}
Foreach($userMembership in $B2EUserGroups.distinguishedName)
{
$Error.Clear()
#Initialize Group and Owner Arrays per object
$B2EGroupMembership = @()
$B2EOwnerMembership = @()
$memberGroupParse = $userMembership -Split(",OU=") -Split("CN=")
#Reference memberGroupName[1] using $memberGroupName, due to split on "CN=".
$memberGroupName = $memberGroupParse[1]
$memberGroupADLDSOU = $memberGroupParse[2]
#Determine what the management OU should be, only supports one sub-OU
If($memberGroupADLDSOU -eq "PortalGroups"){$memberGroupADLDSOU = "OU=PortalGroups,OU=Applications,DC=domain,DC=com"}
Else{$memberGroupADLDSOU = "OU=$memberGroupADLDSOU," + "OU=PortalGroups,OU=Applications,DC=domain,DC=com"}
#Check for Unique Group DisplayName
$FIMDisplayCheck = export-fimconfig -uri $URI `
–onlyBaseResources `
-customconfig "/Group[DisplayName='$memberGroupName']"
If($FIMDisplayCheck){WriteLog "ERROR!: $memberGroupName already exists. Moving to next object."}
Else
{
#Search for people containing object class Person AND membership group AND not containing *-AdminGroup"
WriteLog "Conducting LDAP Search on userMembership $userMembership"
$membershipCapture = LDAPSearch -Filter "(&(objectClass=Person)(memberOf=$userMembership))"`
-Attributes @("cn","distinguishedName")`
-baseDN $PeopleOU
#If group contains AD LDS members, Create and import FIM Object.
If($membershipCapture.cn)
{
#Continue to check valid members
WriteLog "Validating Members"
#Check each user in Membership for FIMURNConvert table.
Foreach($userMember in $membershipCapture.cn)
{
$FIMUserCheck = $null
$FIMUserURN = $null
$FIMURNID = $null
If($FIMURNConvert.item("$userMember"))
{
#User exists within the FIMURNConverttable, record membership to group, add to member count for group
UserLog "member|$userMember|$userMembership"
$B2EGroupMembership += $FIMURNConvert.item("$userMember")
}
Else
{
#Validate user exists in FIM
$FIMUserCheck = export-fimconfig -uri $URI `
–onlyBaseResources `
-customconfig "/Person[AccountName = '$userMember']"
$FIMUserURN = GetAttribute -exportObject $FIMUserCheck -name "objectID"
If($FIMUserURN)
{
#User exists in FIM add to hash table, record membership to group, add to member count for group
$FIMURNConvert.Add($userMember,$FIMUserURN)
UserLog "member|$userMember|$userMembership"
$B2EGroupMembership += $FIMUserURN
}
#Record invalid global ID in log file.
Else{WriteLog "No user exists in FIM with Global ID: $userMember!"}
}
}
#Adding all valid owners to convert table in FIM
WriteLog "Validating Owners"
#If owner member groups contain a group name that starts with member group name
If($ownerM2Gtable.Keys -Like "$ownerGroup*")
{
#Pull the array of owners from the members to group hashtable that should be associated with the group.
$applyOwners = $ownerM2Gtable.Keys -Like "$ownerGroup"
Foreach($owner in $ownerM2Gtable.item("$applyOwners"))
{
If($FIMURNConvert.item("$owner"))
{
#User exists within the FIMURNConvert table, record membership and add to processing array
UserLog "owner|$owner|$userMembership"
$B2EOwnerMembership += $FIMURNConvert.item("$owner")
}
Else
{
#Validate user exists in FIM
$FIMUserCheck = export-fimconfig -uri $URI `
–onlyBaseResources `
-customconfig "/Person[AccountName = '$owner']"
$FIMUserURN = GetAttribute -exportObject $FIMUserCheck -name "objectID"
If($FIMUserURN)
{
#Object exists in FIM, add to hash table and record membership and add to processing array
$FIMURNConvert.Add($owner,$FIMUserURN)
UserLog "owner|$owner|$userMembership"
$B2EOwnerMembership += $FIMUserURN
}
#Record invalid global ID in log file.
Else{WriteLog "No user exists in FIM with Global ID: $owner!"}
}
}
}
Else{WriteLog "There is no -AdminGroup group in the hashtable to reference $ownerGroup. Default owners will be applied."}
If($B2EGroupMembership.Count -gt "0")
{
WriteLog "Creating FIM Object"
$B2ESecurityGroup = CreateObject -objectType "Group"
WriteLog "Adding FIM Base Attributes"
SetAttribute -object $B2ESecurityGroup -attributeName "DisplayName" -attributeValue $memberGroupName
SetAttribute -object $B2ESecurityGroup -attributeName "MembershipLocked" -attributeValue "False"
SetAttribute -object $B2ESecurityGroup -attributeName "Domain" -attributeValue "Domain"
SetAttribute -object $B2ESecurityGroup -attributeName “Scope” -attributeValue "Universal"
SetAttribute -object $B2ESecurityGroup -attributeName “Type” -attributeValue "Security"
SetAttribute -object $B2ESecurityGroup -attributeName “MembershipAddWorkflow" -attributeValue "Owner Approval"
SetAttribute -object $B2ESecurityGroup -attributeName "DisplayedOwner" -attributeValue $B2EOwnerMembership[0]
WriteLog "Applying Owners"
#Add each owner in -AdminGroup group as "Owners" to associated User group on FIM Object.
Foreach($owner in $B2EOwnerMembership){AddMultiValue -object $B2ESecurityGroup -attributeName "Owner" -attributeValue $owner}
WriteLog "Applying Members"
#Add each user in user group, as a "Member" on FIM Object.
Foreach($userMember in $B2EGroupMembership){AddMultiValue -object $B2ESecurityGroup -attributeName "ExplicitMember" -attributeValue $userMember}
WriteLog "Importing $memberGroupName into FIM."
$B2ESecurityGroup | Import-FIMConfig -uri $URI
WriteLog "Complete."
#Add FIMURN and group OU to hashtable for processing at the end
$memberOU2FGtable.add($B2ESecurityGroup.TargetObjectIdentifier,$memberGroupADLDSOU)
$FIMURNID = $B2ESecurityGroup.TargetObjectIdentifier.ToString()
GroupLog "$memberGroupName|$FIMURNID|$memberGroupADLDSOU"
}
Else{WriteLog "ERROR!: No valid members in FIM exist for $memberGroupName"}
}
Else{WriteLog "ERROR!: No members exist in AD LDS for $memberGroupName!"}
}
If($Error)
{
WriteLog "Error(s) identified while processing $memberGroupName in FIM:"
foreach($msg in $Error){WriteLog "$msg`r`n--------"}
}
}
}
WriteLog "Completed creating FIM Security Groups."
WriteLog "Processing extended attribute updates."
Foreach($SecurityGroup in $memberOU2FGtable.Keys)
{
#Clear errors for processing extended attributes.
$Error.Clear
WriteLog "Modifying extended attributes for $SecurityGroup."
#Create object to modify resource.
$extendedAttributes = ModifyObject -ObjectType "Group" -Target $SecurityGroup
#List of extended attributes to modify
SetAttribute -object $extendedAttributes -attributeName "ADLDSorganizationalUnit" -attributeValue $memberOU2FGtable.item($SecurityGroup)
#SetAttribute -object $importObject -attributeName "IsSecurityGroup" -attributeValue "False"
WriteLog "Importing changes."
$extendedAttributes | Import-FIMConfig -uri $URI
WriteLog "Complete."
WriteLog "-----------------"
If($Error)
{
WriteLog "Error(s) identified while processing $memberGroupName in FIM:"
foreach($msg in $Error){WriteLog "$msg`r`n--------"}
}
}
WriteLog "End of script."
WriteLog "Exiting..."
Exit
Note
To provide feedback about this article, create a post on the FIM TechNet Forum.
For more FIM related Windows PowerShell scripts, see the FIM ScriptBox