FIM 2010

Scripted Bulk Update of FIM 2010 Groups

Share

I’ve been required to oversee a bulk update of a few thousand users into a manually managed portal group for which the FIM Portal is authoritative. Anyone who has had to add more than a few users knows this can be a cumbersome process by hand via the UI. To save time from doing this manually through the Portal interface I’ve thrown together a PowerShell script that will read a CSV file of users and operations (add or remove) and process those requests against a group.

The script will write the output of each operation to the console and optionally to a log file. The script is not necessarily intended to be efficient, with reliability being more important, so each user is treated as an individual request to prevent issues with one account failure impacting those of others. I’ve so far only tested this with about 50 accounts, and in my environment each add took a second give or take with no additional workflows being triggered by the update.

The script should be run under the context of a group owner from the server where the FIM Service is installed. If multiple users are matched, an error is output and that user is skipped. Currently groups are matched on DisplayName and AccountName, and users on DisplayName, AccountName or Email. These can be easily modified in the script if required.

Example Usage: Minimum

.\BulkUpdateGroup.ps1 -GroupName 'testGroup' -ImportCSV 'E:\Imports\BulkUpdateUsers.csv'

Example Usage: Log File Specified

.\BulkUpdateGroup.ps1 -GroupName 'testGroup' -ImportCSV 'E:\Imports\BulkUpdateUsers.csv' -LogFilePath 'EL\Imports\BulkUpdateResults.txt'

Example Usage: Comma Delimiter

.\BulkUpdateGroup.ps1 -GroupName 'testGroup'-ImportCSV 'E:\Imports\BulkUpdateUsers.csv'-LogFilePath 'EL\Imports\BulkUpdateResults.txt'-Delimiter ','
Source Code:
PARAM($GroupName, $ImportCSV, $Delimiter = ";", $LogFilePath)
 
 
if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
$ProgressPreference="SilentlyContinue"
 
 
###
### BulkUpdateGroup.ps1
###
### Add or remove users from a Portal Group.
###
### USAGE: .\BulkUpdateGroup.ps1 -GroupName <GroupName> -ImportCSV "<UsersImportFilePath>" [-Delimiter <Delimiter>] [-LogFilePath <LogFile>]
###
### Parameters:     GroupName             Display Name or Account ame of group to update
###                 UsersImportFilePath   FileName to import CSV File (see below for format)
###                 Delimiter (optional)  Delimiter used in CSV file. Default to semicolon (;), but may also be comma (,)
###                 LogFile (optional)    If specified, logs are appended to the specified text file
###
### CSV Format:     Header Row            Two columns - "ChangeType;Identifier"
###                 ChangeType            Either "Add" or "Remove"
###                 Identifier            Display Name, Account Name or EMail address of user to add/remove from the group
###
### Additional Notes: The script will not process users if more than one match is found for any Identifier.
###
 
 
$FIMSvcURI = "http://devapp043vs:5725/ResourceManagementService"
 
 
$GroupIdentifiers = @("AccountName", "DisplayName")
$PersonIdentifiers = @("AccountName", "DisplayName", "Email")
 
 
$LogToFile = $LogFilePath -ne $null;
$ImportOperation = [Microsoft.ResourceManagement.Automation.ObjectModel.ImportOperation]
 
 
Function GetSingleResource
{
    Param($Filter)
    End
    {
        $exportResource = export-fimconfig -uri $FIMSvcURI –onlyBaseResources -customconfig ("$Filter") -ErrorVariable error -ErrorAction SilentlyContinue
        If($error){Throw $error}
        If($exportResource -eq $null) {Throw "Resource not found: $Filter"}
        If(@($exportResource).Count -ne 1) {Throw "More than one resource found: $Filter"}
        $exportResource
    }
}
 
 
Function GetFilterString
{
    Param($ObjectClass, $IdentifierCollection, $SearchValue)
    End
    {
        $filterExpressions = new-object system.collections.arraylist;
         
        foreach ($identifier in $IdentifierCollection)
        {
            $filterExpression = "{0}=""{1}""" -f $identifier,$SearchValue;
            $filterExpressions.Add($filterExpression) > null;
        }
         
        "/{0}[{1}]" -f $ObjectClass, [String]::Join(" or ", $filterExpressions);
    }
}
 
 
Function LogOutput
{
    Param($Message)
    End
    {
        Write-Host $Message;
        if ($LogToFile)
        {
            Add-Content $LogFilePath ((Get-Date).ToString() + " - " + $Message);
        }
    }
}
 
 
## Check Args
if (-not $GroupName)                        {    Write-Host "Must Specify a 'GroupName' value of the group to be updated corresponding to the groups Display Name or Account Name";    exit 1;    }
if (-not $ImportCSV)                        {    Write-Host "Must Specify an 'ImportCSV' file name of users to be processed";    exit 1;    }
 
 
## Load CSV Data
$csvData = Get-Content $ImportCSV | ConvertFrom-Csv -Delimiter $Delimiter
if (-not $csvData)
{
    LogOutput -Message "No users in import CSV file. Exiting";
    exit 1;
}
 
 
## Load Group
$groupFilter = GetFilterString -ObjectClass "Group" -IdentifierCollection $GroupIdentifiers -SearchValue $GroupName;
$groupObject = GetSingleResource -Filter $groupFilter;
 
 
LogOutput -Message ("Successfully found Group with Display Name = '{0}'" -f $GroupName);
LogOutput -Message "Preparing to process user list";
 
 
foreach ($csvEntry in $csvData)
{
    $userObject = $null;
    Try
    {
        $userFilter = GetFilterString -ObjectClass "Person" -IdentifierCollection $PersonIdentifiers -SearchValue $csvEntry.Identifier
        $userObject = GetSingleResource -Filter $userFilter
    }
    Catch
    {
        LogOutput -Message ("Failed to locate an IAM Portal record for '{0}', or more than one entry was found" -f $csvEntry.Identifier);
        Continue;
    }
    Finally
    {
    }
       
    $Operation = $null;
    switch ($csvEntry.ChangeType)
    {
        "Add"      { $Operation = $ImportOperation::Add; }
        "Remove"   { $Operation = $ImportOperation::Delete; }
        Default   
        {
            LogOutput -Message ("Invalid ChangeType for record '{0}'. ChangeType must be either 'Add' or 'Remove'." -f $csvEntry.Identifier);
            Continue;
        }               
    }
 
    $importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange;
    $importChange.Operation      = $Operation;
    $importChange.AttributeName  = "ExplicitMember";
    $importChange.AttributeValue = $userObject.ResourceManagementObject.ObjectIdentifier;
    $importChange.FullyResolved  = 0;
    $importChange.Locale         = "Invariant";
  
    $importObject                        = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject;
    $importObject.ObjectType             = "Group";
    $importObject.TargetObjectIdentifier = $groupObject.ResourceManagementObject.ObjectIdentifier;
    $importObject.SourceObjectIdentifier = $groupObject.ResourceManagementObject.ObjectIdentifier;
    $importObject.State                  = 1 ;
    $ImportObject.Changes                = (,$ImportChange);
    
    $importObject | Import-FIMConfig -Uri $FIMSvcURI  -ErrorVariable Err -ErrorAction SilentlyContinue | Out-Null;
    If($Err)
    {
        LogOutput -Message ("Failed to update group '{0}' in the IAM Portal to include the user '{1}'" -f $GroupName, $csvEntry.Identifier);
    }
    else
    {
        if ($Operation -eq $ImportOperation::Add)
        {
            LogOutput -Message ("Successfully added to group '{0}' in the IAM Portal the user '{1}'" -f $GroupName, $csvEntry.Identifier);
        }
        else
        {
            LogOutput -Message ("Successfully removed from group '{0}' in the IAM Portal the user '{1}'" -f $GroupName, $csvEntry.Identifier);
        }
    }
}

 

CSV File Format
ChangeType;Identifier
Add;"BulkUpload01, Test"
Add;"BulkUpload02, Test"
Add;"BulkUpload03, Test"
Remove;"BulkUpload04, Test"
Remove;"BulkUpload05, Test"

 

The original group update code is derived from the single user example at: http://social.technet.microsoft.com/wiki/contents/articles/2930.how-to-use-powershell-to-add-a-member-to-a-group-in-fim.aspx