Functions/Convert-MimSyncConfigToDsc.ps1

function Convert-MimSyncConfigToDsc {
    <#
.Synopsis
   Convert the Sync Server Configuration XML to PowerShell Desired State Configuration strings
.DESCRIPTION
    1. Read configuration from the ma-data and mv-data XMLs
    2. Generate DSC configuration item strings
.EXAMPLE
   Convert-MimSyncConfigToDsc
.EXAMPLE
   Convert-MimSyncConfigToDsc -Path C:\Temp
#>

    [CmdletBinding()]
    Param
    (
        # Folder with the Sync Service configuration XML files (defaults to $env:ProgramData\MimDsc\Svrexport)
        $Path = "$env:ProgramData\MimDsc\Svrexport"
    )
    Write-Verbose "Using Path: $Path"

    $dscConfigScriptItems = @()

    #region EAF rules
    $eafRules = Get-MimSyncExportAttributeFlow -ServerConfigurationFolder $Path
    
    foreach ($eafRule in $eafRules) {    
        $SyncObjectID = ([Guid]$eafRule.ID).Guid #the curlies will break the DSC configuration string so need to remove them
        switch ($eafRule.RuleType) {
            'direct-mapping' {            
                $dscConfigScriptItems += @'
    ExportAttributeFlowRule {0}
    {{
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        CDAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        SrcAttribute = '{6}'
        SuppressDeletions = ${7}
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $eafRule.MAName
                    $eafRule.MVObjectType
                    $eafRule.CDAttribute
                    $eafRule.CDObjectType
                    $eafRule.RuleType
                    $eafRule.MVAttribute
                    $eafRule.AllowNulls
                )
            
            }
            'scripted-mapping' {
                $mvAttribute = ($eafRule.MVAttribute | ForEach-Object {"'$PSItem'"}) -join ','
                $dscConfigScriptItems += @'
    ExportAttributeFlowRule {0}
    {{
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        CDAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        SrcAttribute = {6}
        SuppressDeletions = ${7}
        ScriptContext = '{8}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $eafRule.MAName
                    $eafRule.MVObjectType
                    $eafRule.CDAttribute
                    $eafRule.CDObjectType
                    $eafRule.RuleType
                    $mvAttribute
                    $eafRule.AllowNulls
                    $eafRule.ScriptContext
                )       
            }  
            'constant-mapping' {            
                $dscConfigScriptItems += @'
    ExportAttributeFlowRule {0}
    {{
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        CDAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        ConstantValue = '{6}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $eafRule.MAName
                    $eafRule.MVObjectType
                    $eafRule.CDAttribute
                    $eafRule.CDObjectType
                    $eafRule.RuleType
                    $eafRule.ConstantValue
                )
            
            }      
            Default {
                Write-Warning "Unexpected EAF rule type: $($eafRule.RuleType)"
            }
        }
    }

    #endregion EAF rules

    #region IAF rules
    $iafRules = Get-MimSyncImportAttributeFlow -ServerConfigurationFolder $Path
    
    foreach ($iafRule in $iafRules) {    
        $SyncObjectID = ([Guid]$iafRule.ID).Guid #the curlies will break the DSC configuration string so need to remove them
        switch ($iafRule.RuleType) {
            'direct-mapping' {            
                $dscConfigScriptItems += @'
    ImportAttributeFlowRule {0}
    {{
        FakeIdentifier = '{0}'
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        MVAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        SrcAttribute = '{6}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $iafRule.MAName
                    $iafRule.MVObjectType
                    $iafRule.MVAttribute
                    $iafRule.CDObjectType
                    $iafRule.RuleType
                    $iafRule.SrcAttribute
                )
            
            }
            'scripted-mapping' {
                $srcAttribute = ($iafRule.SrcAttribute | ForEach-Object {"'$PSItem'"}) -join ','
                $dscConfigScriptItems += @'
    ImportAttributeFlowRule {0}
    {{
        FakeIdentifier = '{0}'
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        MVAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        SrcAttribute = {6}
        ScriptContext = '{7}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $iafRule.MAName
                    $iafRule.MVObjectType
                    $iafRule.MVAttribute
                    $iafRule.CDObjectType
                    $iafRule.RuleType
                    $srcAttribute
                    $iafRule.ScriptContext
                )       
            }  
            'constant-mapping' {            
                $dscConfigScriptItems += @'
    ImportAttributeFlowRule {0}
    {{
        FakeIdentifier = '{0}'
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        MVAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        ConstantValue = '{6}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $iafRule.MAName
                    $iafRule.MVObjectType
                    $iafRule.MVAttribute
                    $iafRule.CDObjectType
                    $iafRule.RuleType
                    $iafRule.ConstantValue
                )          
            }
            'dn-part-mapping' {            
                $dscConfigScriptItems += @'
    ImportAttributeFlowRule {0}
    {{
        FakeIdentifier = '{0}'
        ManagementAgentName = '{1}'
        MVObjectType = '{2}'
        MVAttribute = '{3}'
        CDObjectType = '{4}'
        Type = '{5}'
        DNPart = '{6}'
        Ensure = 'Present'
    }}
'@
 -f @(
                    $SyncObjectID
                    $iafRule.MAName
                    $iafRule.MVObjectType
                    $iafRule.MVAttribute
                    $iafRule.CDObjectType
                    $iafRule.RuleType
                    $iafRule.DNPart
                )          
            }            
            Default {
                Write-Warning "Unexpected IAF rule type: $($iafRule.RuleType)"
            }
        }
    }      

    #endregion IAF rules

    #region Join rules
    $joinRules = Get-MimSyncJoinRule -ServerConfigurationFolder "$env:ProgramData\MimDsc\Svrexport\"

    foreach ($joinRule in $joinRules) {    
        $joinCriteriaStrings = @()
        foreach($joinCriteria in $joinRule.JoinCriterion)
        {
            $attributeMappingStrings = @()
            foreach($attributeMapping in $joinCriteria.AttributeMapping)
            {
                $CDAttribute = ($attributeMapping.CDAttribute | ForEach-Object {"'$PSItem'"}) -join ','
                $attributeMappingStrings += @'
            AttributeMapping{{
                MappingType = '{0}'
                MVAttribute = '{1}'
                CDAttribute = {2}
                ScriptContext = '{3}'
            }}
'@
 -f @(
    $attributeMapping.MappingType
    $attributeMapping.MVAttribute
    $CDAttribute
    $attributeMapping.ScriptContext
)
            }

            $joinCriteriaStrings += @'
        JoinCriterion{{
            ID = {0}
            MVObjectType = '{1}'
            ResolutionType = '{2}'
            ResolutionScriptContext = '{3}'
            Order = {4}
            AttributeMapping = @(
                {5}
            )
        }}
'@
 -f @(
            $joinCriteria.ID
            $joinCriteria.MVObjectType
            $joinCriteria.ResolutionType
            $joinCriteria.ResolutionScriptContext
            $joinCriteria.Order
            ($attributeMappingStrings -join "`n")
        )              
        } 
        $dscConfigScriptItems += @'
    JoinRule {1}
    {{
        ManagementAgentName = '{0}'
        CDObjectType = '{1}'
        JoinCriterion = @(
        {2}
        )
        Ensure = 'Present'
    }}
'@
 -f @(
            $joinRule.MAName
            $joinRule.CDObjectType
            $joinCriteriaStrings -join "`n"            
        )
            
    }
    #endregion Join rules

    #region Projection rules
    $projectionRules = Get-MimSyncProjectionRule -ServerConfigurationFolder "$env:ProgramData\MimDsc\Svrexport\" 
    foreach ($projectionRule in $projectionRules) {
        $dscConfigScriptItems += @'
    ProjectionRule {1}
    {{
        ManagementAgentName = '{0}'
        CDObjectType = '{1}'
        Type = '{2}'
        MVObjectType = '{3}'
        Ensure = 'Present'
    }}
'@
 -f @(
    $projectionRule.MAName
    $projectionRule.CDObjectType
    $projectionRule.Type
    $projectionRule.MVObjectType
)
    }

    #endregion Projection rules

    #region MVOptions

    $mvData = Select-Xml -Path (Join-Path (Get-MimSyncConfigCache) mv.xml) -XPath "//mv-data"
    $dscConfigScriptItems += @'
    MVOptions MimSyncMVOptions
    {{
        FakeIdentifier = 'MVOptions'
        ProvisioningType = '{0}'
        ExtensionAssemblyName = '{1}'
        ExtensionApplicationProtection = '{2}'
    }}
'@
 -f @(
    $mvData.Node.provisioning.type
    $mvData.Node.extension.'assembly-name'
    $mvData.Node.extension.'application-protection'
)
    #endregion MVOptions

    #region MVDeletionRule

    $mvDeletionRules = Get-MimSyncMVDeletionRule
    foreach($mvDeletionRule in Get-MimSyncMVDeletionRule)
    {
        $dscConfigScriptItems += @'
        MVDeletionRule MimSyncMVDeletionRule{0}
        {{
            MVObjectType = '{0}'
            Type = '{1}'
            ManagementAgentName = @({2})
        }}
'@
 -f @(
    $mvDeletionRule.MVObjectType
    $mvDeletionRule.Type
    (($mvDeletionRule.ManagementAgentName | ForEach-Object {"'$PSItem'"}) -join ',')
)
    }

    
    #endregion MVOptions

    #region MAData
    $maDatas = Select-Xml -Path (Join-Path (Get-MimSyncConfigCache) *.xml) -XPath "//ma-data"
    foreach ($maData in $maDatas.Node)
    {
        $dscConfigScriptItems += @'
        MaData MimSyncMAData{0}
        {{
            Name = '{0}'
            AttributeInclusion = @({1})
            Category = '{2}'
            ControllerConfiguration = ControllerConfiguration{{
                ApplicationArchitecture = '{3}'
                ApplicationProtection = '{4}'
            }}
            Extension = Extension{{
                AssemblyName = '{5}'
                ApplicationProtection = '{6}'
            }}
            PasswordSync = PasswordSync{{
                AllowLowSecurity = ${7}
                MaximumRetryCount = {8}
                RetryInterval = {9}
            }}
            PasswordSyncAllowed = ${10}
            ProvisioningCleanup = ProvisioningCleanup{{
                Type = '{11}'
                Action = '{12}'
            }}
            Ensure = 'Present'
        }}
'@
 -f @(
        $maData.name
        (($maData.'attribute-inclusion'.attribute | ForEach-Object {"'$PSItem'"}) -join ",`n`t")
        $maData.category
        $maData.'controller-configuration'.'application-architecture'
        $maData.'controller-configuration'.'application-protection'
        $maData.extension.'assembly-name'
        $maData.extension.'application-protection'
        ($maData.'password-sync'.'allow-low-security' -as [int] -as [Boolean])
        $maData.'password-sync'.'maximum-retry-count'
        $maData.'password-sync'.'retry-interval'
        ($maData.'password-sync-allowed' -as [int] -as [Boolean])
        $maData.'provisioning-cleanup'.type
        $maData.'provisioning-cleanup'.action
        )
    }
    #endregion MAData

    #region MVObjectType
    $namespace = @{dsml="http://www.dsml.org/DSML"; 'ms-dsml'="http://www.microsoft.com/MMS/DSML"}

    $mvObjectTypes = Select-Xml -Path (Join-Path (Get-MimSyncConfigCache) mv.xml) -XPath "//mv-data/schema/dsml:dsml/dsml:directory-schema/dsml:class" -Namespace $namespace
    foreach ($mvObjectType in $mvObjectTypes.Node)
    {
        $mvAttributes = ($mvObjectType.attribute | ForEach-Object { "MVAttributeBinding {ID='$($PSItem.ref)'; Required=`$$($PSItem.required)}"}) -join "`n`t"
        $dscConfigScriptItems += @'
        MVObjectType MVObjectType{0}
        {{
            ID = '{0}'
            Type = '{1}'
            Attributes = @(
                {2}
            )
            Ensure = 'Present'
        }}
'@
 -f @(
        $mvObjectType.ID
        $mvObjectType.Type
        $mvAttributes
        )
    }
    #endregion MVObjectType

    #region MVAttributeType
    $namespace = @{dsml="http://www.dsml.org/DSML"; 'ms-dsml'="http://www.microsoft.com/MMS/DSML"}

    $mvAttributeTypes = Select-Xml -Path (Join-Path (Get-MimSyncConfigCache) mv.xml) -XPath "//mv-data/schema/dsml:dsml/dsml:directory-schema/dsml:attribute-type" -Namespace $namespace
    foreach ($mvAttributeType in $mvAttributeTypes.Node)
    {
        $singleValue = 'false'
        if ($mvAttributeType.'single-value')
        {
            $singleValue = $mvAttributeType.'single-value'
        }

        $indexed = 'false'
        if ($mvAttributeType.indexed)
        {
            $indexed = $mvAttributeType.indexed
        }

        $indexable = 'false'
        if ($mvAttributeType.indexable)
        {
            $indexable = $mvAttributeType.indexable
        }

        $dscConfigScriptItems += @'
        MVAttributeType MVAttributeType{0}
        {{
            ID = '{0}'
            SingleValue = ${1}
            Indexable = ${2}
            Indexed = ${3}
            Syntax = '{4}'
            Ensure = 'present'
        }}
'@
 -f @(
        $mvAttributeType.id
        $singleValue
        $indexable
        $indexed
        $mvAttributeType.syntax
        )
    }
    #endregion MVAttributeType

    #region RunProfile
    $runProfiles = Get-MimSyncRunProfile

    foreach ($runProfile in $runProfiles) {    
        $runStepStrings = @()
        foreach($runStep in $runProfile.RunSteps)
        {
            $runStepStrings += @'
        RunStep{{
            StepType = '{0}'
            StepSubType = @({1})
            PartitionIdentifier = '{2}'
            InputFile = '{3}'
            PageSize = {4}
            Timeout = {5}
            ObjectDeleteLimit = {6}
            ObjectProcessLimit = {7}
            LogFilePath = '{8}'
            DropFileName = '{9}'
            FakeIdentifier = '{10}'
        }}
'@
 -f @(
            $runStep.StepType
            ($runStep.StepSubType | ForEach-Object { "'$PSItem'"}) -join ','
            $runStep.PartitionIdentifier
            $runStep.InputFile
            $runStep.PageSize
            $runStep.Timeout
            $runStep.ObjectDeleteLimit
            $runStep.ObjectProcessLimit
            $runStep.LogFilePath
            $runStep.DropFileName
            [Guid]::NewGuid().Guid
        )              
        } 
        $dscConfigScriptItems += @'
    RunProfile '[{0}]{1}'
    {{
        ManagementAgentName = '{0}'
        Name = '{1}'
        RunSteps = @(
        {2}
        )
        Ensure = 'Present'
    }}
'@
 -f @(
            $runProfile.ManagementAgentName
            $runProfile.Name
            $runStepStrings -join "`n"            
        )           
    }

    #endregion RunProfile

    #region ImportAttributePrecedence
    $iafRules = Get-MimSyncImportAttributeFlow -ServerConfigurationFolder $Path | Sort-Object MVObjectType, MVAttribute, PrecedenceRank
    $iafRules | Group-Object MVObjectType, MVAttribute | Where-Object Count -GT 1 | ForEach-Object {
        $mvObjectType = $PSItem.Group[0].MVObjectType
        $mvAttributeType = $PSItem.Group[0].MVAttribute
        $Type = $PSItem.Group[0].PrecedenceType

        if ($Type -eq 'ranked')
        {
            $rankedStrings = @()
            foreach($rankedItem in $PSItem.Group)
            {
                $rankedStrings += "RankedPrecedenceOrder{{Order = {0}; ManagementAgentName='{1}'; CDObjectType='{2}'; ID='{3}'}}" -f ($rankedItem.PrecedenceRank -1), $rankedItem.MAName, $rankedItem.CDObjectType, $rankedItem.ID
            }

            $dscConfigScriptItems += @'
            ImportAttributePrecedence '{0}-{1}'
            {{
                MVObjectType = '{0}'
                MVAttribute = '{1}'
                Type = '{2}'
                RankedPrecedenceOrder = @(
                    {3}
                )
            }}
'@
 -f @(
            $mvObjectType
            $mvAttributeType
            $Type
            $rankedStrings -join "`n`t"
            )
        }
        else
        {
            $dscConfigScriptItems += @'
            ImportAttributePrecedence '{0}-{1}'
            {{
                MVObjectType = '{0}'
                MVAttribute = '{1}'
                Type = '{2}'
            }}
'@
 -f @(
            $mvObjectType
            $mvAttributeType
            $Type
            )
        }
    }
    #endregion ImportAttributePrecedence

    #region MAData
    $maDatas = Select-Xml -Path (Join-Path (Get-MimSyncConfigCache) *.xml) -XPath "//ma-data"
    foreach ($maData in $maDatas.Node)
    {
        foreach ($maPartition in $maData.'ma-partition-data'.partition)
        {  
                                                                                                                    $dscConfigScriptItems += @'
        MimSyncMAPartitionData '[{0}]{1}'
        {{
            ManagementAgentName = '{0}'
            Name = '{1}'
            Selected = ${2}
            ObjectClassInclusions = @(
                {3}
            )
            ContainerExclusions = @(
                {4}
            )
            ContainerInclusions = @(
                {5}
            )
            Ensure = 'Present'
        }}
'@
 -f @(
            $maData.name
            $maPartition.name
            $maPartition.selected -as [Int] -as [Boolean]
            (($maPartition.filter.'object-classes'.'object-class' | ForEach-Object {"'$PSItem'"}) -join ",`n`t")
            (($maPartition.filter.containers.exclusions.exclusion | ForEach-Object {"'$PSItem'"}) -join ",`n`t")
            (($maPartition.filter.containers.inclusions.inclusion | ForEach-Object {"'$PSItem'"}) -join ",`n`t")            
            )
        }
    }
    #endregion MAData

    #region MAPrivateConfiguration
    Write-Warning 'Skipping MAPrivateConfiguration...'
    #endregion MAPrivateConfiguration

    $dscConfigScriptItems

}