Share via


How to Use PowerShell as an FIM Function Library

This page provides a set of PowerShell Functions which can be used as a shared library across PowerShell scripts or from the PowerShell prompt.

Introduction

Here on the TechNet Wiki quit some "How to Use PowerShell to ..." articles have been written in the past. A lot of them allow you to interact with the FIM Portal. You can manage users, groups, sets, MPRs, Schema, … They all use one or more of the following operations:

  • Create
  • Modify
  • Delete

All these operations require some specific PowerShell code in order to be performed successfully. I don’t think there are a lot of people who can actually remember what code to use for what operation. So probably you just take your last script, copy paste it, and then start modifying it until it meet your requirements. Often this gives cluttered/dirty scripts in my opinion.
Now what if we could provide a library with most of the common operations already build in. Those operations could be available just like regular PowerShell cmdlets. Just as easy to use as Export-FIMConfig or Write-Host!

FIMFunctionLibrary

For this purpose the FIMFunctionLibrary was created. In fact it’s just a collection of PowerShell Functions. If you would execute it as a PowerShell script, it will do absolutely nothing. But we can make these functions available in another script or in the prompt by “Dot-Sourcing” it. If I would have to describe Dot-sourcing, I would say it's like adding extra DLLs in a .net project by using references.

Here's a sample piece of code which can be used to dot-source the library when it's located in the same directory as your script. You can put this at the top of your scripts:

001
002
003
$curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
$dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
. $dotSource

On the other hand, if you just want to be able to use the Functions from the PowerShell command prompt like regular cmdlets, all you have to do is execute the following command:

Mind the two dots and the space in between! Now in the command prompt you can use “tab-completion” to use the Functions. Just like a regular cmdlet! Here’s an example:

Getting the properties of an object:

Deleting that object:

Now how easy is that?! The FIMFunctionLibary.ps1 consists of the following functions:

  • Interact with Export-FimConfig:
    • Convert-FimExportToPSObject
    • Delete-FimObject
  • Interact with Import-FimConfig:
    • Prepare-FimObject
    • Set-FimAttribute
    • Commit-FimObject
  • Helper Function:
    • Get-FimObjectID

And here is the library: Copy-Paste and save it as FIMFunctionLibrary.ps1

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
###########################################################################
#
# Library Version 0.5
#
#
#
# Library usage: The Functions can be used by "dot-sourcing" this .ps1 file.
# Scripts should be executed on a server with the FIM Service installed.
#
# From within Scripts, requires the FIMFunctionLibrary.ps1 to be in the same directory as the script
#
# $curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
# $dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
# . $dotSource
#
# From within a PowerShell Prompt
#
# . ./FIMFunctionLibrary.ps1
# or when referencing a specific path:
# . d:\directory\./FIMFunctionLibrary.ps1
#
#

#----------------------------------------------------------------------------------------------------------
set-variable -name URI -value "http://localhost:5725/resourcemanagementservice' " -option constant 
# Can be used to create filter criteria for sets (prepend)
set-variable -name PREFILTER -value "<Filter xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" Dialect=""http://schemas.microsoft.com/2006/11/XPathFilterDialect"" http://schemas.xmlsoap.org/ws/2004/09/enumeration"">" -option constant 
# Can be used to create filter criteria for sets (append)
set-variable -name POSTFILTER -value "</Filter>"  -option constant 
#----------------------------------------------------------------------------------------------------------

# Convert-FimExportToPSObject
# Author: Craig Martin
# Source: http://www.identitytrench.com/2011/07/convert-fim-exportobject-to-powershell.html
#
# Parameters:
# ExportObject: an object coming from the Export-FimConfig cmdlet
#
# Outcome:
# An PSobject which contains all properties from the inputobject available as . formation
# e.g. $psObject.DisplayName
#
Function Convert-FimExportToPSObject
{
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline = $true)]
        [Microsoft.ResourceManagement.Automation.ObjectModel.ExportObject]
        $ExportObject
    )
    Process
    {        
        $psObject = New-Object PSObject
        $ExportObject.ResourceManagementObject.ResourceManagementAttributes | ForEach-Object{
            if ($_.Value -ne $null)
            {
                $value = $_.Value
            }
            elseif($_.Values -ne $null)
            {
                $value = $_.Values
            }
            else
            {
                $value = $null
            }
            $psObject | Add-Member -MemberType NoteProperty -Name $_.AttributeName -Value $value
        }
        Write-Output $psObject
    }


# Delete-FimObject
# Author: Community
# Source: TechNet Wiki
#
# Parameters:
# Object: an object coming from the Export-FimConfig cmdlet
#
# Outcome:
# The Object provided in the command is deleted from the Portal
#
Function Delete-FimObject
{
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline = $true)]
[Microsoft.ResourceManagement.Automation.ObjectModel.ExportObject]
        $Object
    )
    Process
    {       
if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
clear-host
 
$psObject = $Object | Convert-FimExportToPSObject
        $objectId = (($psObject.ObjectID).split(":"))[2]
$objectType = $psObject.ObjectType

$DeleteObject = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
$DeleteObject.ObjectType = $objectType       
$DeleteObject.TargetObjectIdentifier = $objectId
$DeleteObject.SourceObjectIdentifier = $objectId
$DeleteObject.State = 'Delete'
$DeleteObject | Import-FIMConfig -uri $URI

trap 

    $exMessage = $_.Exception.Message
    if($exMessage.StartsWith("L:"))
    {write-host "`n" $exMessage.substring(2) "`n" -foregroundcolor white -backgroundcolor darkblue}
    else {write-host "`nError: " $exMessage "`n" -foregroundcolor white -backgroundcolor darkred}
    #Exit
}
    }
}

# Prepare-FimObject
# Author: Community
# Source: TechNet Wiki
#
# Parameters:
# ObjectType: A String representing an object type in the Portal [Required]
# e.g. Person, Set, SynchronizationRule
# FimObjectID: A String representing the ObjectID of an existing object in the Portal [Optional]
#
# If only the ObjectType is specified a new object will be prepared. If a FimObjectID is specified
# a change to an existing object is prepared
#
# Outcome:
# An empty object which can be used in the Set-FimAttribute or Create-FimObject Functions
#
Function Prepare-FimObject
{
    Param
    (
        [parameter(Position=0, Mandatory=$true)]
[String]
$ObjectType,
[parameter(Position=1, Mandatory=$false, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[String]
$ObjectID
    )
    Process
    {       
$Object = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject
       $Object.ObjectType = $objectType

#New Object
if(!$ObjectID){
$Object.SourceObjectIdentifier = [System.Guid]::NewGuid().ToString()
       }
#Existing Object
else{
$Object.TargetObjectIdentifier = $ObjectID
     $Object.SourceObjectIdentifier = $ObjectID
     $Object.State = 'Put'
}
return $Object
    }
}

# Set-FimAttribute
# Author: Community
# Source: TechNet Wiki
#
# Parameters:
# Object: A valid import object, typically coming from the Prepare-FimObject Function
# AttributeName: The name of the attribute to populate
# AttributeValue: A String value (empty string is allowed)
#
# Outcome:
# The input object with the change appended which in turn can be used in the Set-FimAttribute
# or Create-FimObject Functions
#
Function Set-FimAttribute
 {
    Param
    (
        [parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)]
        [ValidateNotNull()]
[Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject]
$object, 
[parameter(Position=1, Mandatory=$true)]
[String]
$attributeName, 
[parameter(Position=2, Mandatory=$true)]
[AllowEmptyString()]
[String]
$attributeValue
    )
    Process
    {      
        $importChange = New-Object Microsoft.ResourceManagement.Automation.ObjectModel.ImportChange
        $importChange.Operation = 'Replace'
        $importChange.AttributeName = $attributeName
        $importChange.AttributeValue = $attributeValue
        $importChange.FullyResolved = 1
        $importChange.Locale = "Invariant"
        if ($object.Changes -eq $null) {
$object.Changes = (,$importChange)
}
        else {
$object.Changes += $importChange
}
return $object
    }
}

# Create-FimObject
# Author: Community
# Source: TechNet Wiki
#
# Parameters:
# Object: A valid import object, typically coming from the Prepare-FimObject or Set-FimAttribute Function
#
# Outcome:
# The object is created in the Portal
#
Function Commit-FimObject
{
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline = $true)]
        [Microsoft.ResourceManagement.Automation.ObjectModel.ImportObject]
$object
    )
    Process
    {       
 if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
 clear-host
 $object | Import-FIMConfig -uri $URI
 
 trap 

    $exMessage = $_.Exception.Message
    if($exMessage.StartsWith("L:"))
    {write-host "`n" $exMessage.substring(2) "`n" -foregroundcolor white -backgroundcolor darkblue}
    else {write-host "`nError: " $exMessage "`n" -foregroundcolor white -backgroundcolor darkred}
    #Exit
}
    }
}

# Get-FimObjectID
# Author: Thomas Vuylsteke
# Source: /
#
# Parameters:
# Filter: A string representing an Xpath Filter
#
# Outcome:
# The objectID of the object matching the filter.
# An empty string is returned when multiple objects match the filter
#
Function Get-FimObjectID
{
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline = $true)]
        [String]
$filter
    )
    Process
    {       
 if(@(get-pssnapin | where-object {$_.Name -eq "FIMAutomation"} ).count -eq 0) {add-pssnapin FIMAutomation}
 clear-host
 
 $FimObject = export-fimconfig -uri $URI `
                                –onlyBaseResources `
                                -customconfig $filter;
 if(($FimObject -ne $null) -and ($FimObject.Count -lt 2)){
  $psObject = $FimObject |Convert-FimExportToPSObject
  $objectID = (($psObject.ObjectID).split(":"))[2]
return $ObjectID
 }
 elseif($FimObject.Count -gt 1){
  throw "L1:Get-FimObjectID: More than one object is returned from the filter"
 }
 else{
  return $null
 }
 trap 

    $exMessage = $_.Exception.Message
    if($exMessage.StartsWith("L1:")){
write-host "`n" $exMessage.substring(3) "`n" -foregroundcolor white -backgroundcolor darkblue
return ""
}
    else {
write-host "`nError: " $exMessage "`n" -foregroundcolor white -backgroundcolor darkred
}
    #Exit
}
    }
}

# PowerShell Knowledge bit #1:
# The Mandatory attribute blocks null and empty values and prompts you for a value.
# To allow empty values (including null) add the AllowEmptyString parameter attribute
# ValidateNotNullOrEmpty is redundant when Mandatory is specified

# PowerShell Knowledge bit #2:
# From http://technet.microsoft.com/en-us/library/ff394140(WS.10).aspx [Craig Martin]
# Powershell does a pretty good job using Enums, and there are two cases for them in the FIM cmdlets:
# [Enum]::GetValues('Microsoft.ResourceManagement.Automation.ObjectModel.ImportOperation')
# [Enum]::GetNames('Microsoft.ResourceManagement.Automation.ObjectModel.ImportState')
# Many of the FIM PowerShell samples here in TechNet could be more readable if they took advantage of this. For example, instead of passing the number for the Object State, or Change.Operation, you can specify the string, or the value directly from the enum. In the case of 'Add':
# [Microsoft.ResourceManagement.Automation.ObjectModel.ImportOperation]::Add or just
# 'Add'

# http://technet.microsoft.com/en-us/library/ff720152(WS.10).aspx
# States
# 0 = Create
# 1 = Put
# 2 = Delete
# 3 = Resolve
# 4 = None

# Operations
# 0 = Add
# 1 = Replace
# 2 = Delete

 

The Functions Explained

Convert-FimExportToPSOjbect

This function was written by Craig Martin. He provided it on his blog and gives a good explanation of how to use it: Convert a FIM ExportObject to a PowerShell PSObject

Delete-FimObject

The Delete-FimObject is pretty straightforward. All you have to do is feed it some output objects of the Export-FIMConfig cmdlet. Think very well about your xpath filter. If it's wrong you end up deleting the wrong object. As a general best practice consider executing Export-FIMConfig ... | Convert-FimExportToPSObject first so you can get an overview of all objects in the scope of your xpath filter. 

Prepare-FimObject > Set-FimAttribute > Commit-FimObject

These are typically all used together. In the past you had to start by creating an “ImportObject”. This is what Prepare-FimObject will do. You feed it an ObjectType and it gives you a base object which you can use to create a new object in the Portal. On the other hand if you also provide it an existing ObjectID, it can be used to perform updates to an existing object in the Portal.

The Set-FimAttribute allows to add a Replace operation for a given attribute to the prepared object. You can execute this one multiple times in a row in order to prepare multiple attributes.

And finally the object can be exported to the Portal by executing the Commit-FimObject Function.

Get-FimObjectID

The Get-FimObjectID function can be used to retrieve the ObjectID for an object which matches the xpath filter provided to the function. This can be used when creating or updating objects which use attributes that are in fact references to other objects.  

Examples

These are typically all used together. In the past you had to start by creating an “ImportObject”. This is what will do. You feed it an ObjectType and it gives you a base object which you can use to create a new object in the Portal. On the other hand if you also provide it an existing ObjectID, it can be used to perform updates to an existing object in the Portal.The allows to add a Replace operation for a given attribute to the prepared object. You can execute this one multiple times in a row in order to prepare multiple attributes.And finally the object can be exported to the Portal by executing the Function.  

Create a Criteria-Based Set

Here's the equivalent without the FIMFunctionLibrary: How to Use PowerShell to Create a Criteria-Based Set

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
$curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
$dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
. $dotSource

#Prepare the object
$object = Prepare-FimObject "Set"

#Populate attributes
Set-FimAttribute -object $object -attributeName "DisplayName" -attributeValue "Test Set"

$xPathFilter = "/Person[EmployeeType = 'Contractor']"  
$filter = $PREFILTER + $xPathFilter + $POSTFILTER
Set-FimAttribute -object $object -attributeName "Filter" -attributeValue $filter

#Create the object in FIM
Commit-FimObject $object

Create a User

Here's the equivalent without the FIMFunctionLibrary: How to Use PowerShell to Create a User in the FIM Portal

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
$curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
$dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
. $dotSource

if($args.count -ne 1) {throw "You need to specify your attribute values as parameter"}
$attributes = ($args[0]).split("|")

if(0 -ne [String]::Compare(($attributes[0]).split(":")[0],"displayname", $true)){
throw "You need to specify a display name"


$objectName = ($attributes[0]).split(":")[1] 
$exportObject = export-fimconfig -uri $URI `
                                  –onlyBaseResources `
                                  -customconfig "/Person[DisplayName='$objectName']"
if($exportObject) {throw "L:User $objectName already exists"}

#Prepare the object
$object = Prepare-FimObject "Person"

#Populate attributes
foreach($attribute in $attributes)
 { 
    $attrData = $attribute.split(":")
    Set-FimAttribute -object $object -attributeName $($attrData[0]) -attributeValue $($attrData[1])
 }

#Create the object in FIM
Commit-FimObject $object

Create an Outbound Synchronization Rule

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
$curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
$dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
. $dotSource

#Create Outbound Synchronization Rule
$ObjectType = "SynchronizationRule"

#Required Variables
$DisplayName = "Test Sync Rule"
$FlowType = 1 #0 Inbound, 1 outbound, 2 Inbound/Outbound
#MV Resource Type
$ILMObjectType = "person"
#External System
$ConnectedSystem = "{09A22997-1E65-4745-9259-DE047EF3E524}"
#External System Resource Type
$ConnectedObjectType = "user"

#Optional Variables
#Dependency (other OSR)
#$Dependency = "urn:uuid:a91ecb11-d692-4bb1-ae9b-a2cd56956ce1"
#Relationship Criteria
$RelationshipCriteria = "<conditions><condition><ilmAttribute>accountName</ilmAttribute><csAttribute>sAMAccountName</csAttribute></condition></conditions>"
#Create Resource in FIM
$CreateILMObject = $false
#Create Resource in External System
$CreateConnectedSystemObject = $false
#Enable Deprovisioning
$DisconnectConnectedSystemObject = $false

#Attribute Flows
$PersistentFlow = "<export-flow><src><attr>accountName</attr></src><dest>sAMAccountName</dest><scoping></scoping></export-flow>"

#$ManagementAgentID = "urn:uuid:657745e6-8d15-4cfb-889e-1cca82d7d69d"

$object = Prepare-FimObject $ObjectType
Set-FimAttribute -Object $object -AttributeName "DisplayName" -AttributeValue $DisplayName
Set-FimAttribute -Object $object -AttributeName "FlowType" -AttributeValue $FlowType
Set-FimAttribute -Object $object -AttributeName "ILMObjectType" -AttributeValue $ILMObjectType
Set-FimAttribute -Object $object -AttributeName "ConnectedSystem" -AttributeValue $ConnectedSystem
Set-FimAttribute -Object $object -AttributeName "ConnectedObjectType" -AttributeValue $ConnectedObjectType
Set-FimAttribute -Object $object -AttributeName "RelationshipCriteria" -AttributeValue $RelationshipCriteria
Set-FimAttribute -Object $object -AttributeName "CreateILMObject" -AttributeValue $CreateILMObject
Set-FimAttribute -Object $object -AttributeName "CreateConnectedSystemObject" -AttributeValue $CreateConnectedSystemObject
Set-FimAttribute -Object $object -AttributeName "DisconnectConnectedSystemObject" -AttributeValue $DisconnectConnectedSystemObject
Commit-FimObject $object

Update a User Property

001
002
003
004
005
006
007
008
009
$curDir = Split-Path -parent $MyInvocation.MyCommand.Definition
$dotSource = $curDir + "\./FIMFunctionLibrary.ps1"
. $dotSource

##Update Properties##
Get-FimObjectID "/Person[AccountName='vuylthom001']" | `
Prepare-FimObject "Person" | `
Set-FimAttribute -AttributeName "DisplayName" -AttributeValue "Updated T.V." | `
Commit-FimObject

 

Creating a User From The Prompt

Now here's a lot of piping, but you can actually create a user from the commandprompt by just executing this command:

Prepare-FimObject "Person" | Set-FimAttribute -attributeName "DisplayName" -attributeValue "Thomas Vuylsteke Demo" |Set-FimAttribute -attributeName "AccountName" -attributeValue "vuylthom003" | Commit-FimObject

To do

There are certainly other interesting functions to add:

  • Add a value to a multivalued attribute function
  • GetSingleResource function
  • Logging function
  • ...

Feel free to contribute!


See Also