Friday, May 31, 2013

Another update to FIM Powershell module for end-to-end attribute flows

Here's another tweak to the Get-FimSyncConfiguration.psm1 module to include export attribute flows with constant sync-rule values in the end-to-end attribute flows. See highlighted sections below.

Reference version:
FIM PowerShell Modules v2.1
http://fimpowershellmodule.codeplex.com/releases/view/98374

Get-ExportAttributeFlow

<#
  .SYNOPSIS
  Gets the Export Attribute Flow Rules from Sync Server Configuration
  .DESCRIPTION
  Reads the server configuration from the XML files, and outputs theExport Attribute Flow rules as PSObjects
  .OUTPUTS
  PSObjects containing the synchronization server export attribute flowrules
  
  .EXAMPLE
  Get-ExportAttributeFlow -ServerConfigurationFolder"E:\sd\IAM\ITAuthorize\Source\Configuration\FimSync\ServerConfiguration"
#>
Function Get-ExportAttributeFlow
{
  Param
  (       
        [parameter(Mandatory=$false)]
        [String]
        [ValidateScript({Test-Path $_})]
        $ServerConfigurationFolder
  )
  End
  {   
        ### This is where the rules will beaggregated before we output them
        $rules = @()
       
        ### Export attribute flow rules arecontained in the ma-data nodes of the MA*.XML files
        $maFiles = @(get-item (Join-Path $ServerConfigurationFolder "MA-*.xml"))
       
       
        foreach ($maFile in $maFiles)
        {
            ### Get the MA Name and MA ID
            $maName = (select-xml $maFile -XPath "//ma-data/name").Node.InnerText
          
            foreach($exportFlowSet in (Select-Xml -path $maFile -XPath "//export-flow-set" | select -ExpandProperty Node))
            {
                $mvObjectType = $exportFlowSet.'mv-object-type'
                $cdObjectType = $exportFlowSet.'cd-object-type'
               
                foreach($exportFlow in $exportFlowSet.'export-flow')
                {
                    $cdAttribute = $exportFlow.'cd-attribute'
                    [bool]$allowNulls = $false
                    if ([bool]::TryParse($exportFlow.'suppress-deletions', [ref]$allowNulls))
                    {
                        $allowNulls = -not $allowNulls
                    }
                   
                    if ($exportFlow.'direct-mapping' -ne $null)
                    {
                        ###
                        ### Handlesrc-attribute that are intrinsic (object-id)
                        ###
                        if ($exportFlow.'direct-mapping'.'src-attribute'.intrinsic)
                        {
                          $srcAttribute = "<{0}>" -F $exportFlow.'direct-mapping'.'src-attribute'.'#text'
                        }
                        else
                        {
                            $srcAttribute = $exportFlow.'direct-mapping'.'src-attribute'
                        }
                   
                        $rule = New-Object PSObject
                        $rule | Add-Member -MemberType noteproperty -name 'RuleType' -value 'DIRECT'
                        $rule | Add-Member -MemberType noteproperty -name 'MAName' -value $maName               
                        $rule | Add-Member -MemberType noteproperty -name 'MVObjectType' -value $mvObjectType
                        $rule | Add-Member -MemberType noteproperty -name 'MVAttribute' -value $srcAttribute
                        $rule | Add-Member -MemberType noteproperty -name 'CDObjectType' -value $cdObjectType
                        $rule | Add-Member -MemberType noteproperty -name 'CDAttribute' -value $cdAttribute
                        $rule | Add-Member -MemberType noteproperty -name 'ScriptContext' -value $null
                        $rule | Add-Member -MemberType noteproperty -name 'AllowNulls' -value $allowNulls
                       
                        $rules += $rule
                    }
                    elseif ($exportFlow.'scripted-mapping' -ne $null)
                    {               
                        $scriptContext = $exportFlow.'scripted-mapping'.'script-context'                       
                        $srcAttributes = @()
                   
                        ###
                        ### Handlesrc-attribute that are intrinsic (object-id)
                        ###
                        $exportFlow.'scripted-mapping'.'src-attribute' | ForEach-Object {
                        if ($_.intrinsic)
                        {
                            $srcAttributes += "<{0}>" -F $_.'#text'
                        }
                        elseif ($_) # Do not addempty values.
                        {
                            $srcAttributes += $_
                        }
                    }
                    # (Commented)Leave as collection.
                    #if($srcAttributes.Count -eq 1)
                    #{
                    #    $srcAttributes = $srcAttributes -as[String]
                    #}
                       
                    $rule = New-Object PSObject
                    $rule | Add-Member -MemberType noteproperty -name 'RuleType' -value 'SCRIPTED'
                    $rule | Add-Member -MemberType noteproperty -name 'MAName' -value $maName
                    $rule | Add-Member -MemberType noteproperty -name 'MVObjectType' -value $mvObjectType
                    $rule | Add-Member -MemberType noteproperty -name 'MVAttribute' -value $srcAttributes
                    $rule | Add-Member -MemberType noteproperty -name 'CDObjectType' -value $cdObjectType
                    $rule | Add-Member -MemberType noteproperty -name 'CDAttribute' -value $cdAttribute
                    $rule | Add-Member -MemberType noteproperty -name 'ScriptContext' -value $scriptContext
                    $rule | Add-Member -MemberType noteproperty -name 'AllowNulls' -value $allowNulls
                                   
                    $rules += $rule                       
                }
                    elseif ($exportFlow.'sync-rule-mapping' -ne $null)
                    {
                        $srcAttribute = $exportFlow.'sync-rule-mapping'.'src-attribute'
                        if($exportFlow.'sync-rule-mapping'.'mapping-type' -eq 'direct')
                        {
                            $rule = New-Object PSObject
                            $rule | Add-Member -MemberType noteproperty -name 'RuleType' -value 'OSR-Direct'
                            $rule | Add-Member -MemberType noteproperty -name 'MAName' -value $maName
                            $rule | Add-Member -MemberType noteproperty -name 'MVObjectType' -value $mvObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'MVAttribute' -value $srcAttribute
                            $rule | Add-Member -MemberType noteproperty -name 'CDObjectType' -value $cdObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'CDAttribute' -value $cdAttribute                                                    
                            $rule | Add-Member -MemberType noteproperty -name 'ScriptContext' -value $null
                            $rule | Add-Member -MemberType noteproperty -name 'AllowNulls' -value $allowNulls
                                            
                            $rules += $rule            
                        }
                        elseif ($exportFlow.'sync-rule-mapping'.'mapping-type' -eq 'expression')
                        {
                            $scriptContext = $exportFlow.'sync-rule-mapping'.'sync-rule-value'.'export-flow'.InnerXml
                            $srcAttribute = $exportFlow.'sync-rule-mapping'.'sync-rule-value'.'export-flow'.dest
                            $rule = New-Object PSObject
                            $rule | Add-Member -MemberType noteproperty -name 'RuleType' -value 'OSR-Expression'
                            $rule | Add-Member -MemberType noteproperty -name 'MAName' -value $maName
                            $rule | Add-Member -MemberType noteproperty -name 'MVObjectType' -value $mvObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'MVAttribute' -value $srcAttribute
                            $rule | Add-Member -MemberType noteproperty -name 'CDObjectType' -value $cdObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'CDAttribute' -value $cdAttribute                                                    
                            $rule | Add-Member -MemberType noteproperty -name 'ScriptContext' -value $scriptContext
                            $rule | Add-Member -MemberType noteproperty -name 'AllowNulls' -value $allowNulls
                                           
                            $rules += $rule            
                        }
                        elseif ($exportFlow.'sync-rule-mapping'.'mapping-type' -eq 'constant')
                        {                      
                            $srcAttributes = @()
                            $scriptContext = $exportFlow.'sync-rule-mapping'.'sync-rule-value'
                            $rule = New-Object PSObject
                            $rule | Add-Member -MemberType noteproperty -name 'RuleType' -value 'OSR-Constant'
                            $rule | Add-Member -MemberType noteproperty -name 'MAName' -value $maName
                            $rule | Add-Member -MemberType noteproperty -name 'MVObjectType' -value $mvObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'MVAttribute' -value $srcAttributes
                            $rule | Add-Member -MemberType noteproperty -name 'CDObjectType' -value $cdObjectType
                            $rule | Add-Member -MemberType noteproperty -name 'CDAttribute' -value $cdAttribute                                                    
                            $rule | Add-Member -MemberType noteproperty -name 'ScriptContext' -value "'$scriptContext'"
                            $rule | Add-Member -MemberType noteproperty -name 'AllowNulls' -value $allowNulls
                                           
                            $rules += $rule            
                        }
                        else
                        {
                            throw "UnsupportedExport Flow type '$($exportFlow.'sync-rule-mapping'.'mapping-type')'"
                        }
                          
                    }
                }
            }
        }
       
        Write-Output $rules
    }#End
}

5 comments:

  1. In the Join-ImportToExportAttributeFlow function, is there really a need for "EAFMVAttribute"?

    There's 3 scenarios for each attrbute in the function IAF+EAF, IAF, EAF.

    For IAF+EAF MVAttribute is part of the matching so no need for EAFMVAttribute there.

    For IAF there is no EAF and it's set to null so no need for EAFMVAttribute there.

    For EAF wouldn't it make more sense to map it to plain "MVAttribute" since there's no IAF mapping to it? Also would be good to include "MVObjectType" in this one since there's no IAF contributing as well. MVAttribute and MVObjectType should be required independent of whether output is for IAF,EAF, or IAF+EAF.



    Is there a reason -contains is used in the matching of MVAttribute? Shouldn't they be exact like MVObjectType? Could have attributes like email, personalEmail,companyEmail, etc where email could match up with other attributes.

    ReplyDelete
    Replies
    1. In the Join-ImportToExportAttributeFlow function, is there really a need for "EAFMVAttribute"?

      Thanks for the feedback. The difference between MVAttribute and EAFMVAttribute is that MVAttribute will always contain a single attribute, whereas EAFMVAttribute will contain the full list of MV attributes that contribute to a single EAF.

      To give you an example of how this would be helpful, I ultimately dump these end-to-end flow rules into an Excel spreadsheet, where I can slice and dice them. Having the two separate columns allows me to (a) filter by a single MV attribute while at the same time (b) see the full list of MV attributes that contribute to each EAF.

      Is there a reason -contains is used in the matching of MVAttribute? Shouldn't they be exact like MVObjectType?

      The Powershell -contains operator matches by complete item. In order to do a wildcard search (as you demonstrate), you would do -like "*email*".

      Delete
  2. One last thing. For each IAF all matching EAFs are "linked". This means if you have more than one IAF for a unique MVObjectType/MVAttribute combo, then the same EAFs will be output more than once.

    IAFs should be matched together same way each IAF is matched to EAFs.

    Maybe a counter for matches to add properties such as IAFRuleType1,IAFSourceMA1,etc. Then each output object will be end to end for each MVObjectType/MVAttribute combo. Also would remove the duplicated EAFs

    ReplyDelete
    Replies
    1. As I mentioned in my previous comment, exposing all possible circuits for end-to-end flow rules allows you to slice and dice the data very nicely in Excel. If this doesn't suit your needs, I encourage you to develop a new function and (if you'd like) submit it to the Codeplex team as a contribution.

      Delete
  3. Ah I totally didn't think about multiple MV attributes combined for EAF! I had to add that back into my version of the function. Thanks for pointing that out.

    Glad to understand the reasoning for making it work the way it does. I did use this to make my own similar function with an output that returns an array of PSObjects. Each has a MVObjectType,MVAttribute, IAFs (array of matching IAFs), and EAFs (array of matching EAFs).

    I'll look at getting it submitted to Codeplex.

    Here's a sample output that's readable if an array of objects isn't what you want output.


    ------------------------------------------------------------------------------
    MVObjectType : group
    MVAttribute : displayName
    IAFs
    ----
    RuleType : DIRECT
    SourceMA : FIMMA
    CDObjectType : Group
    CDAttribute : DisplayName
    ScriptContext :
    PrecedenceType : equal
    PrecedenceRank :
    Matched : False

    RuleType : ISR - direct
    SourceMA : ADMA
    CDObjectType : group
    CDAttribute : cn
    ScriptContext :
    PrecedenceType : equal
    PrecedenceRank :
    Matched : True

    EAFs
    ----
    RuleType : DIRECT
    MAName : FIMMA
    CDObjectType : Group
    CDAttribute : DisplayName
    ScriptContext :
    AllowNulls : False
    Matched : True

    RuleType : OSR-Direct
    MAName : ADMA
    CDObjectType : group
    CDAttribute : displayName
    ScriptContext :
    AllowNulls : False
    Matched : True

    ------------------------------------------------------------------------------

    ReplyDelete