Share via


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